1 : /******************************************************************************
2 : * $Id: cpl_odbc.cpp 19175 2010-03-24 17:50:16Z chaitanya $
3 : *
4 : * Project: OGR ODBC Driver
5 : * Purpose: Declarations for ODBC Access Cover API.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2003, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_odbc.h"
31 : #include "cpl_vsi.h"
32 : #include "cpl_string.h"
33 : #include "cpl_error.h"
34 :
35 :
36 : #ifndef WIN32CE /* ODBC is not supported on Windows CE. */
37 :
38 : CPL_CVSID("$Id: cpl_odbc.cpp 19175 2010-03-24 17:50:16Z chaitanya $");
39 :
40 : #ifndef SQLColumns_TABLE_CAT
41 : #define SQLColumns_TABLE_CAT 1
42 : #define SQLColumns_TABLE_SCHEM 2
43 : #define SQLColumns_TABLE_NAME 3
44 : #define SQLColumns_COLUMN_NAME 4
45 : #define SQLColumns_DATA_TYPE 5
46 : #define SQLColumns_TYPE_NAME 6
47 : #define SQLColumns_COLUMN_SIZE 7
48 : #define SQLColumns_BUFFER_LENGTH 8
49 : #define SQLColumns_DECIMAL_DIGITS 9
50 : #define SQLColumns_NUM_PREC_RADIX 10
51 : #define SQLColumns_NULLABLE 11
52 : #define SQLColumns_REMARKS 12
53 : #define SQLColumns_COLUMN_DEF 13
54 : #define SQLColumns_SQL_DATA_TYPE 14
55 : #define SQLColumns_SQL_DATETIME_SUB 15
56 : #define SQLColumns_CHAR_OCTET_LENGTH 16
57 : #define SQLColumns_ORDINAL_POSITION 17
58 : #define SQLColumns_IS_NULLABLE 18
59 : #endif /* ndef SQLColumns_TABLE_CAT */
60 :
61 : /************************************************************************/
62 : /* CPLODBCDriverInstaller() */
63 : /************************************************************************/
64 :
65 0 : CPLODBCDriverInstaller::CPLODBCDriverInstaller()
66 0 : : m_nUsageCount(0)
67 : {
68 0 : memset( m_szPathOut, '\0', ODBC_FILENAME_MAX );
69 0 : memset( m_szError, '\0', SQL_MAX_MESSAGE_LENGTH );
70 0 : }
71 :
72 : /************************************************************************/
73 : /* InstallDriver() */
74 : /************************************************************************/
75 :
76 : int CPLODBCDriverInstaller::InstallDriver( const char* pszDriver,
77 0 : const char* pszPathIn, WORD fRequest )
78 : {
79 0 : CPLAssert( NULL != pszDriver );
80 :
81 : // Try to install driver to system-wide location
82 0 : if ( FALSE == SQLInstallDriverEx( pszDriver, NULL, m_szPathOut,
83 : ODBC_FILENAME_MAX, NULL, fRequest,
84 : &m_nUsageCount ) )
85 : {
86 0 : const WORD nErrorNum = 1; // TODO - a function param?
87 0 : RETCODE cRet = SQL_ERROR;
88 :
89 : // Failure is likely related to no write permissions to
90 : // system-wide default location, so try to install to HOME
91 :
92 : static char* pszEnvIni = NULL;
93 0 : if (pszEnvIni == NULL)
94 : {
95 : // Read HOME location
96 0 : char* pszEnvHome = NULL;
97 0 : pszEnvHome = getenv("HOME");
98 :
99 0 : CPLAssert( NULL != pszEnvHome );
100 0 : CPLDebug( "ODBC", "HOME=%s", pszEnvHome );
101 :
102 : // Set ODBCSYSINI variable pointing to HOME location
103 0 : pszEnvIni = (char *)CPLMalloc( strlen(pszEnvHome) + 12 );
104 :
105 0 : sprintf( pszEnvIni, "ODBCSYSINI=%s", pszEnvHome );
106 : /* a 'man putenv' shows that we cannot free pszEnvIni */
107 : /* because the pointer is used directly by putenv in old glibc */
108 0 : putenv( pszEnvIni );
109 :
110 0 : CPLDebug( "ODBC", "%s", pszEnvIni );
111 : }
112 :
113 : // Try to install ODBC driver in new location
114 0 : if ( FALSE == SQLInstallDriverEx( pszDriver, NULL, m_szPathOut,
115 : ODBC_FILENAME_MAX, NULL, fRequest,
116 : &m_nUsageCount ) )
117 : {
118 : cRet = SQLInstallerError( nErrorNum, &m_nErrorCode,
119 0 : m_szError, SQL_MAX_MESSAGE_LENGTH, NULL );
120 0 : CPLAssert( SQL_SUCCESS == cRet || SQL_SUCCESS_WITH_INFO == cRet );
121 :
122 : // FAIL
123 0 : return FALSE;
124 : }
125 : }
126 :
127 : // SUCCESS
128 0 : return TRUE;
129 : }
130 :
131 : /************************************************************************/
132 : /* RemoveDriver() */
133 : /************************************************************************/
134 :
135 0 : int CPLODBCDriverInstaller::RemoveDriver( const char* pszDriverName, int fRemoveDSN )
136 : {
137 0 : CPLAssert( NULL != pszDriverName );
138 :
139 0 : if ( FALSE == SQLRemoveDriver( pszDriverName, fRemoveDSN, &m_nUsageCount ) )
140 : {
141 0 : const WORD nErrorNum = 1; // TODO - a function param?
142 :
143 : // Retrieve error code and message
144 : SQLInstallerError( nErrorNum, &m_nErrorCode,
145 0 : m_szError, SQL_MAX_MESSAGE_LENGTH, NULL );
146 :
147 0 : return FALSE;
148 : }
149 :
150 : // SUCCESS
151 0 : return TRUE;
152 : }
153 :
154 : /************************************************************************/
155 : /* CPLODBCSession() */
156 : /************************************************************************/
157 :
158 0 : CPLODBCSession::CPLODBCSession()
159 :
160 : {
161 0 : m_szLastError[0] = '\0';
162 0 : m_hEnv = NULL;
163 0 : m_hDBC = NULL;
164 0 : }
165 :
166 : /************************************************************************/
167 : /* ~CPLODBCSession() */
168 : /************************************************************************/
169 :
170 0 : CPLODBCSession::~CPLODBCSession()
171 :
172 : {
173 0 : CloseSession();
174 0 : }
175 :
176 : /************************************************************************/
177 : /* CloseSession() */
178 : /************************************************************************/
179 :
180 0 : int CPLODBCSession::CloseSession()
181 :
182 : {
183 0 : if( m_hDBC!=NULL )
184 : {
185 0 : CPLDebug( "ODBC", "SQLDisconnect()" );
186 0 : SQLDisconnect( m_hDBC );
187 0 : SQLFreeConnect( m_hDBC );
188 0 : m_hDBC = NULL;
189 : }
190 :
191 0 : if( m_hEnv!=NULL )
192 : {
193 0 : SQLFreeEnv( m_hEnv );
194 0 : m_hEnv = NULL;
195 : }
196 :
197 0 : return TRUE;
198 : }
199 :
200 : /************************************************************************/
201 : /* Failed() */
202 : /* */
203 : /* Test if a return code indicates failure, return TRUE if that */
204 : /* is the case. Also update error text. */
205 : /************************************************************************/
206 :
207 0 : int CPLODBCSession::Failed( int nRetCode, HSTMT hStmt )
208 :
209 : {
210 : SQLCHAR achSQLState[SQL_MAX_MESSAGE_LENGTH];
211 : SQLINTEGER nNativeError;
212 0 : SQLSMALLINT nTextLength=0;
213 :
214 0 : m_szLastError[0] = '\0';
215 :
216 0 : if( nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO )
217 0 : return FALSE;
218 :
219 : SQLError( m_hEnv, m_hDBC, hStmt, achSQLState, &nNativeError,
220 : (SQLCHAR *) m_szLastError, sizeof(m_szLastError)-1,
221 0 : &nTextLength );
222 0 : m_szLastError[nTextLength] = '\0';
223 :
224 0 : return TRUE;
225 : }
226 :
227 : /************************************************************************/
228 : /* EstablishSession() */
229 : /************************************************************************/
230 :
231 : /**
232 : * Connect to database and logon.
233 : *
234 : * @param pszDSN The name of the DSN being used to connect. This is not
235 : * optional.
236 : *
237 : * @param pszUserid the userid to logon as, may be NULL if not not required,
238 : * or provided by the DSN.
239 : *
240 : * @param pszPassword the password to logon with. May be NULL if not required
241 : * or provided by the DSN.
242 : *
243 : * @return TRUE on success or FALSE on failure. Call GetLastError() to get
244 : * details on failure.
245 : */
246 :
247 : int CPLODBCSession::EstablishSession( const char *pszDSN,
248 : const char *pszUserid,
249 0 : const char *pszPassword )
250 :
251 : {
252 0 : CloseSession();
253 :
254 0 : if( Failed( SQLAllocEnv( &m_hEnv ) ) )
255 0 : return FALSE;
256 :
257 0 : if( Failed( SQLAllocConnect( m_hEnv, &m_hDBC ) ) )
258 : {
259 0 : CloseSession();
260 0 : return FALSE;
261 : }
262 :
263 0 : SQLSetConnectOption( m_hDBC,SQL_LOGIN_TIMEOUT,5 );
264 :
265 0 : if( pszUserid == NULL )
266 0 : pszUserid = "";
267 0 : if( pszPassword == NULL )
268 0 : pszPassword = "";
269 :
270 : int bFailed;
271 0 : if( strstr(pszDSN,"=") != NULL )
272 : {
273 : SQLCHAR szOutConnString[1024];
274 0 : SQLSMALLINT nOutConnStringLen = 0;
275 :
276 0 : CPLDebug( "ODBC", "SQLDriverConnect(%s)", pszDSN );
277 : bFailed = Failed(
278 : SQLDriverConnect( m_hDBC, NULL,
279 : (SQLCHAR *) pszDSN, (SQLSMALLINT)strlen(pszDSN),
280 : szOutConnString, sizeof(szOutConnString),
281 0 : &nOutConnStringLen, SQL_DRIVER_NOPROMPT ) );
282 : }
283 : else
284 : {
285 0 : CPLDebug( "ODBC", "SQLConnect(%s)", pszDSN );
286 : bFailed = Failed(
287 : SQLConnect( m_hDBC, (SQLCHAR *) pszDSN, SQL_NTS,
288 : (SQLCHAR *) pszUserid, SQL_NTS,
289 0 : (SQLCHAR *) pszPassword, SQL_NTS ) );
290 : }
291 :
292 0 : if( bFailed )
293 : {
294 0 : CPLDebug( "ODBC", "... failed: %s", GetLastError() );
295 0 : CloseSession();
296 0 : return FALSE;
297 : }
298 :
299 0 : return TRUE;
300 : }
301 :
302 : /************************************************************************/
303 : /* GetLastError() */
304 : /************************************************************************/
305 :
306 : /**
307 : * Returns the last ODBC error message.
308 : *
309 : * @return pointer to an internal buffer with the error message in it.
310 : * Do not free or alter. Will be an empty (but not NULL) string if there is
311 : * no pending error info.
312 : */
313 :
314 0 : const char *CPLODBCSession::GetLastError()
315 :
316 : {
317 0 : return m_szLastError;
318 : }
319 :
320 : /************************************************************************/
321 : /* ==================================================================== */
322 : /* CPLODBCStatement */
323 : /* ==================================================================== */
324 : /************************************************************************/
325 :
326 : /************************************************************************/
327 : /* CPLODBCStatement() */
328 : /************************************************************************/
329 :
330 0 : CPLODBCStatement::CPLODBCStatement( CPLODBCSession *poSession )
331 :
332 : {
333 0 : m_poSession = poSession;
334 :
335 0 : if( Failed(
336 : SQLAllocStmt( poSession->GetConnection(), &m_hStmt ) ) )
337 : {
338 0 : m_hStmt = NULL;
339 0 : return;
340 : }
341 :
342 0 : m_nColCount = 0;
343 0 : m_papszColNames = NULL;
344 0 : m_panColType = NULL;
345 0 : m_papszColTypeNames = NULL;
346 0 : m_panColSize = NULL;
347 0 : m_panColPrecision = NULL;
348 0 : m_panColNullable = NULL;
349 :
350 0 : m_papszColValues = NULL;
351 0 : m_panColValueLengths = NULL;
352 :
353 0 : m_pszStatement = NULL;
354 0 : m_nStatementMax = 0;
355 0 : m_nStatementLen = 0;
356 : }
357 :
358 : /************************************************************************/
359 : /* ~CPLODBCStatement() */
360 : /************************************************************************/
361 :
362 0 : CPLODBCStatement::~CPLODBCStatement()
363 :
364 : {
365 0 : Clear();
366 :
367 0 : if( m_hStmt != NULL )
368 0 : SQLFreeStmt( m_hStmt, SQL_DROP );
369 0 : }
370 :
371 : /************************************************************************/
372 : /* ExecuteSQL() */
373 : /************************************************************************/
374 :
375 : /**
376 : * Execute an SQL statement.
377 : *
378 : * This method will execute the passed (or stored) SQL statement,
379 : * and initialize information about the resultset if there is one.
380 : * If a NULL statement is passed, the internal stored statement that
381 : * has been previously set via Append() or Appendf() calls will be used.
382 : *
383 : * @param pszStatement the SQL statement to execute, or NULL if the
384 : * internally saved one should be used.
385 : *
386 : * @return TRUE on success or FALSE if there is an error. Error details
387 : * can be fetched with OGRODBCSession::GetLastError().
388 : */
389 :
390 0 : int CPLODBCStatement::ExecuteSQL( const char *pszStatement )
391 :
392 : {
393 0 : if( m_poSession == NULL || m_hStmt == NULL )
394 : {
395 : // we should post an error.
396 0 : return FALSE;
397 : }
398 :
399 0 : if( pszStatement != NULL )
400 : {
401 0 : Clear();
402 0 : Append( pszStatement );
403 : }
404 :
405 0 : if( Failed(
406 : SQLExecDirect( m_hStmt, (SQLCHAR *) m_pszStatement, SQL_NTS ) ) )
407 0 : return FALSE;
408 :
409 0 : return CollectResultsInfo();
410 : }
411 :
412 : /************************************************************************/
413 : /* CollectResultsInfo() */
414 : /************************************************************************/
415 :
416 0 : int CPLODBCStatement::CollectResultsInfo()
417 :
418 : {
419 0 : if( m_poSession == NULL || m_hStmt == NULL )
420 : {
421 : // we should post an error.
422 0 : return FALSE;
423 : }
424 :
425 0 : if( Failed( SQLNumResultCols(m_hStmt,&m_nColCount) ) )
426 0 : return FALSE;
427 :
428 : /* -------------------------------------------------------------------- */
429 : /* Allocate per column information. */
430 : /* -------------------------------------------------------------------- */
431 0 : m_papszColNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
432 0 : m_papszColValues = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
433 0 : m_panColValueLengths = (_SQLLEN *) CPLCalloc(sizeof(_SQLLEN),(m_nColCount+1));
434 :
435 0 : m_panColType = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
436 0 : m_papszColTypeNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
437 0 : m_panColSize = (_SQLULEN *) CPLCalloc(sizeof(_SQLULEN),m_nColCount);
438 0 : m_panColPrecision = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
439 0 : m_panColNullable = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
440 :
441 : /* -------------------------------------------------------------------- */
442 : /* Fetch column descriptions. */
443 : /* -------------------------------------------------------------------- */
444 0 : for( SQLUSMALLINT iCol = 0; iCol < m_nColCount; iCol++ )
445 : {
446 : SQLCHAR szName[256];
447 0 : SQLSMALLINT nNameLength = 0;
448 :
449 0 : if ( Failed( SQLDescribeCol(m_hStmt, iCol+1,
450 : szName, sizeof(szName), &nNameLength,
451 : m_panColType + iCol,
452 : m_panColSize + iCol,
453 : m_panColPrecision + iCol,
454 : m_panColNullable + iCol) ) )
455 0 : return FALSE;
456 :
457 0 : szName[nNameLength] = '\0'; // Paranoid; the string should be
458 : // null-terminated by the driver
459 0 : m_papszColNames[iCol] = CPLStrdup((const char*)szName);
460 :
461 : // SQLDescribeCol() fetches just a subset of column attributes.
462 : // In addition to above data we need data type name.
463 0 : if ( Failed( SQLColAttribute(m_hStmt, iCol + 1, SQL_DESC_TYPE_NAME,
464 : szName, sizeof(szName),
465 : &nNameLength, NULL) ) )
466 0 : return FALSE;
467 :
468 0 : szName[nNameLength] = '\0'; // Paranoid
469 0 : m_papszColTypeNames[iCol] = CPLStrdup((const char*)szName);
470 :
471 : // CPLDebug( "ODBC", "%s %s %d", m_papszColNames[iCol],
472 : // szName, m_panColType[iCol] );
473 : }
474 :
475 0 : return TRUE;
476 : }
477 :
478 : /************************************************************************/
479 : /* GetColCount() */
480 : /************************************************************************/
481 :
482 : /**
483 : * Fetch the resultset column count.
484 : *
485 : * @return the column count, or zero if there is no resultset.
486 : */
487 :
488 0 : int CPLODBCStatement::GetColCount()
489 :
490 : {
491 0 : return m_nColCount;
492 : }
493 :
494 : /************************************************************************/
495 : /* GetColName() */
496 : /************************************************************************/
497 :
498 : /**
499 : * Fetch a column name.
500 : *
501 : * @param iCol the zero based column index.
502 : *
503 : * @return NULL on failure (out of bounds column), or a pointer to an
504 : * internal copy of the column name.
505 : */
506 :
507 0 : const char *CPLODBCStatement::GetColName( int iCol )
508 :
509 : {
510 0 : if( iCol < 0 || iCol >= m_nColCount )
511 0 : return NULL;
512 : else
513 0 : return m_papszColNames[iCol];
514 : }
515 :
516 : /************************************************************************/
517 : /* GetColType() */
518 : /************************************************************************/
519 :
520 : /**
521 : * Fetch a column data type.
522 : *
523 : * The return type code is a an ODBC SQL_ code, one of SQL_UNKNOWN_TYPE,
524 : * SQL_CHAR, SQL_NUMERIC, SQL_DECIMAL, SQL_INTEGER, SQL_SMALLINT, SQL_FLOAT,
525 : * SQL_REAL, SQL_DOUBLE, SQL_DATETIME, SQL_VARCHAR, SQL_TYPE_DATE,
526 : * SQL_TYPE_TIME, SQL_TYPE_TIMESTAMPT.
527 : *
528 : * @param iCol the zero based column index.
529 : *
530 : * @return type code or -1 if the column is illegal.
531 : */
532 :
533 0 : short CPLODBCStatement::GetColType( int iCol )
534 :
535 : {
536 0 : if( iCol < 0 || iCol >= m_nColCount )
537 0 : return -1;
538 : else
539 0 : return m_panColType[iCol];
540 : }
541 :
542 : /************************************************************************/
543 : /* GetColTypeName() */
544 : /************************************************************************/
545 :
546 : /**
547 : * Fetch a column data type name.
548 : *
549 : * Returns data source-dependent data type name; for example, "CHAR",
550 : * "VARCHAR", "MONEY", "LONG VARBINAR", or "CHAR ( ) FOR BIT DATA".
551 : *
552 : * @param iCol the zero based column index.
553 : *
554 : * @return NULL on failure (out of bounds column), or a pointer to an
555 : * internal copy of the column dat type name.
556 : */
557 :
558 0 : const char *CPLODBCStatement::GetColTypeName( int iCol )
559 :
560 : {
561 0 : if( iCol < 0 || iCol >= m_nColCount )
562 0 : return NULL;
563 : else
564 0 : return m_papszColTypeNames[iCol];
565 : }
566 :
567 : /************************************************************************/
568 : /* GetColSize() */
569 : /************************************************************************/
570 :
571 : /**
572 : * Fetch the column width.
573 : *
574 : * @param iCol the zero based column index.
575 : *
576 : * @return column width, zero for unknown width columns.
577 : */
578 :
579 0 : short CPLODBCStatement::GetColSize( int iCol )
580 :
581 : {
582 0 : if( iCol < 0 || iCol >= m_nColCount )
583 0 : return -1;
584 : else
585 0 : return (short) m_panColSize[iCol];
586 : }
587 :
588 : /************************************************************************/
589 : /* GetColPrecision() */
590 : /************************************************************************/
591 :
592 : /**
593 : * Fetch the column precision.
594 : *
595 : * @param iCol the zero based column index.
596 : *
597 : * @return column precision, may be zero or the same as column size for
598 : * columns to which it does not apply.
599 : */
600 :
601 0 : short CPLODBCStatement::GetColPrecision( int iCol )
602 :
603 : {
604 0 : if( iCol < 0 || iCol >= m_nColCount )
605 0 : return -1;
606 : else
607 0 : return m_panColPrecision[iCol];
608 : }
609 :
610 : /************************************************************************/
611 : /* GetColNullable() */
612 : /************************************************************************/
613 :
614 : /**
615 : * Fetch the column nullability.
616 : *
617 : * @param iCol the zero based column index.
618 : *
619 : * @return TRUE if the column may contains or FALSE otherwise.
620 : */
621 :
622 0 : short CPLODBCStatement::GetColNullable( int iCol )
623 :
624 : {
625 0 : if( iCol < 0 || iCol >= m_nColCount )
626 0 : return -1;
627 : else
628 0 : return m_panColNullable[iCol];
629 : }
630 :
631 : /************************************************************************/
632 : /* Fetch() */
633 : /************************************************************************/
634 :
635 : /**
636 : * Fetch a new record.
637 : *
638 : * Requests the next row in the current resultset using the SQLFetchScroll()
639 : * call. Note that many ODBC drivers only support the default forward
640 : * fetching one record at a time. Only SQL_FETCH_NEXT (the default) should
641 : * be considered reliable on all drivers.
642 : *
643 : * Currently it isn't clear how to determine whether an error or a normal
644 : * out of data condition has occured if Fetch() fails.
645 : *
646 : * @param nOrientation One of SQL_FETCH_NEXT, SQL_FETCH_LAST, SQL_FETCH_PRIOR,
647 : * SQL_FETCH_ABSOLUTE, or SQL_FETCH_RELATIVE (default is SQL_FETCH_NEXT).
648 : *
649 : * @param nOffset the offset (number of records), ignored for some
650 : * orientations.
651 : *
652 : * @return TRUE if a new row is successfully fetched, or FALSE if not.
653 : */
654 :
655 0 : int CPLODBCStatement::Fetch( int nOrientation, int nOffset )
656 :
657 : {
658 0 : ClearColumnData();
659 :
660 0 : if( m_hStmt == NULL || m_nColCount < 1 )
661 0 : return FALSE;
662 :
663 : /* -------------------------------------------------------------------- */
664 : /* Fetch a new row. Note that some brain dead drives (such as */
665 : /* the unixodbc text file driver) don't implement */
666 : /* SQLScrollFetch(), so we try to stick to SQLFetch() if we */
667 : /* can). */
668 : /* -------------------------------------------------------------------- */
669 : SQLRETURN nRetCode;
670 :
671 0 : if( nOrientation == SQL_FETCH_NEXT && nOffset == 0 )
672 : {
673 0 : nRetCode = SQLFetch( m_hStmt );
674 0 : if( Failed(nRetCode) )
675 : {
676 0 : if ( nRetCode != SQL_NO_DATA )
677 : {
678 : CPLError( CE_Failure, CPLE_AppDefined, "%s",
679 0 : m_poSession->GetLastError() );
680 : }
681 0 : return FALSE;
682 : }
683 : }
684 : else
685 : {
686 0 : nRetCode = SQLFetchScroll(m_hStmt, (SQLSMALLINT) nOrientation, nOffset);
687 0 : if( Failed(nRetCode) )
688 : {
689 0 : if ( nRetCode == SQL_NO_DATA )
690 : {
691 : CPLError( CE_Failure, CPLE_AppDefined, "%s",
692 0 : m_poSession->GetLastError() );
693 : }
694 0 : return FALSE;
695 : }
696 : }
697 :
698 : /* -------------------------------------------------------------------- */
699 : /* Pull out all the column values. */
700 : /* -------------------------------------------------------------------- */
701 : SQLSMALLINT iCol;
702 :
703 0 : for( iCol = 0; iCol < m_nColCount; iCol++ )
704 : {
705 : char szWrkData[513];
706 : _SQLLEN cbDataLen;
707 0 : SQLSMALLINT nFetchType = GetTypeMapping( m_panColType[iCol] );
708 :
709 : // Handle values other than WCHAR and BINARY as CHAR.
710 0 : if( nFetchType != SQL_C_BINARY && nFetchType != SQL_C_WCHAR )
711 0 : nFetchType = SQL_C_CHAR;
712 :
713 0 : szWrkData[0] = '\0';
714 0 : szWrkData[sizeof(szWrkData)-1] = '\0';
715 :
716 : nRetCode = SQLGetData( m_hStmt, iCol + 1, nFetchType,
717 : szWrkData, sizeof(szWrkData)-1,
718 0 : &cbDataLen );
719 :
720 : /* SQLGetData() is giving garbage values in the first 4 bytes of cbDataLen *
721 : * in some architectures. Converting it to (int) discards the unnecessary *
722 : * bytes. This should not be a problem unless the buffer size reaches *
723 : * 2GB. (#3385) */
724 0 : cbDataLen = (int) cbDataLen;
725 :
726 0 : if( Failed( nRetCode ) )
727 : {
728 0 : if ( nRetCode == SQL_NO_DATA )
729 : {
730 : CPLError( CE_Failure, CPLE_AppDefined, "%s",
731 0 : m_poSession->GetLastError() );
732 : }
733 0 : return FALSE;
734 : }
735 :
736 0 : if( cbDataLen == SQL_NULL_DATA )
737 : {
738 0 : m_papszColValues[iCol] = NULL;
739 0 : m_panColValueLengths[iCol] = 0;
740 : }
741 :
742 : // assume big result: should check for state=SQLSATE 01004.
743 0 : else if( nRetCode == SQL_SUCCESS_WITH_INFO )
744 : {
745 0 : if( cbDataLen >= (_SQLLEN)(sizeof(szWrkData)-1) )
746 : {
747 0 : cbDataLen = (_SQLLEN)(sizeof(szWrkData)-1);
748 0 : if (nFetchType == SQL_C_CHAR)
749 0 : while ((cbDataLen > 1) && (szWrkData[cbDataLen - 1] == 0))
750 0 : --cbDataLen; // trimming the extra terminators: bug 990
751 : }
752 :
753 0 : m_papszColValues[iCol] = (char *) CPLMalloc(cbDataLen+1);
754 0 : memcpy( m_papszColValues[iCol], szWrkData, cbDataLen );
755 0 : m_papszColValues[iCol][cbDataLen] = '\0';
756 0 : m_panColValueLengths[iCol] = cbDataLen;
757 :
758 0 : while( TRUE )
759 : {
760 : _SQLLEN nChunkLen;
761 :
762 : nRetCode = SQLGetData( m_hStmt, (SQLUSMALLINT) iCol+1,
763 : nFetchType,
764 : szWrkData, sizeof(szWrkData)-1,
765 0 : &cbDataLen );
766 0 : if( nRetCode == SQL_NO_DATA )
767 0 : break;
768 :
769 0 : if( Failed( nRetCode ) )
770 : {
771 : CPLError( CE_Failure, CPLE_AppDefined, "%s",
772 0 : m_poSession->GetLastError() );
773 0 : return FALSE;
774 : }
775 :
776 0 : if( cbDataLen >= (int) (sizeof(szWrkData) - 1)
777 : || cbDataLen == SQL_NO_TOTAL )
778 : {
779 0 : nChunkLen = sizeof(szWrkData)-1;
780 0 : if (nFetchType == SQL_C_CHAR)
781 0 : while ( (nChunkLen > 1)
782 : && (szWrkData[nChunkLen - 1] == 0) )
783 0 : --nChunkLen; // trimming the extra terminators
784 : }
785 : else
786 0 : nChunkLen = cbDataLen;
787 0 : szWrkData[nChunkLen] = '\0';
788 :
789 : m_papszColValues[iCol] = (char *)
790 : CPLRealloc( m_papszColValues[iCol],
791 0 : m_panColValueLengths[iCol] + nChunkLen + 2 );
792 : memcpy( m_papszColValues[iCol] + m_panColValueLengths[iCol],
793 0 : szWrkData, nChunkLen );
794 0 : m_panColValueLengths[iCol] += nChunkLen;
795 0 : m_papszColValues[iCol][m_panColValueLengths[iCol]] = '\0';
796 0 : m_papszColValues[iCol][m_panColValueLengths[iCol]+1] = '\0';
797 : }
798 : }
799 : else
800 : {
801 0 : m_panColValueLengths[iCol] = cbDataLen;
802 0 : m_papszColValues[iCol] = (char *) CPLMalloc(cbDataLen+2);
803 0 : memcpy( m_papszColValues[iCol], szWrkData, cbDataLen );
804 0 : m_papszColValues[iCol][cbDataLen] = '\0';
805 0 : m_papszColValues[iCol][cbDataLen+1] = '\0';
806 : }
807 :
808 : // Trim white space off end, if there is any.
809 0 : if( nFetchType == SQL_C_CHAR && m_papszColValues[iCol] != NULL )
810 : {
811 0 : char *pszTarget = m_papszColValues[iCol];
812 0 : size_t iEnd = strlen(pszTarget);
813 :
814 0 : while ( iEnd > 0 && pszTarget[iEnd - 1] == ' ' )
815 0 : pszTarget[--iEnd] = '\0';
816 : }
817 :
818 : // Convert WCHAR to UTF-8, assuming the WCHAR is UCS-2.
819 0 : if( nFetchType == SQL_C_WCHAR && m_papszColValues[iCol] != NULL
820 : && m_panColValueLengths[iCol] > 0 )
821 : {
822 0 : wchar_t *pwszSrc = (wchar_t *) m_papszColValues[iCol];
823 :
824 : m_papszColValues[iCol] =
825 0 : CPLRecodeFromWChar( pwszSrc, CPL_ENC_UCS2, CPL_ENC_UTF8 );
826 0 : m_panColValueLengths[iCol] = strlen(m_papszColValues[iCol]);
827 :
828 0 : CPLFree( pwszSrc );
829 : }
830 : }
831 :
832 0 : return TRUE;
833 : }
834 :
835 : /************************************************************************/
836 : /* GetColData() */
837 : /************************************************************************/
838 :
839 : /**
840 : * Fetch column data.
841 : *
842 : * Fetches the data contents of the requested column for the currently loaded
843 : * row. The result is returned as a string regardless of the column type.
844 : * NULL is returned if an illegal column is given, or if the actual column
845 : * is "NULL".
846 : *
847 : * @param iCol the zero based column to fetch.
848 : *
849 : * @param pszDefault the value to return if the column does not exist, or is
850 : * NULL. Defaults to NULL.
851 : *
852 : * @return pointer to internal column data or NULL on failure.
853 : */
854 :
855 0 : const char *CPLODBCStatement::GetColData( int iCol, const char *pszDefault )
856 :
857 : {
858 0 : if( iCol < 0 || iCol >= m_nColCount )
859 0 : return pszDefault;
860 0 : else if( m_papszColValues[iCol] != NULL )
861 0 : return m_papszColValues[iCol];
862 : else
863 0 : return pszDefault;
864 : }
865 :
866 : /************************************************************************/
867 : /* GetColData() */
868 : /************************************************************************/
869 :
870 : /**
871 : * Fetch column data.
872 : *
873 : * Fetches the data contents of the requested column for the currently loaded
874 : * row. The result is returned as a string regardless of the column type.
875 : * NULL is returned if an illegal column is given, or if the actual column
876 : * is "NULL".
877 : *
878 : * @param pszColName the name of the column requested.
879 : *
880 : * @param pszDefault the value to return if the column does not exist, or is
881 : * NULL. Defaults to NULL.
882 : *
883 : * @return pointer to internal column data or NULL on failure.
884 : */
885 :
886 : const char *CPLODBCStatement::GetColData( const char *pszColName,
887 0 : const char *pszDefault )
888 :
889 : {
890 0 : int iCol = GetColId( pszColName );
891 :
892 0 : if( iCol == -1 )
893 0 : return pszDefault;
894 : else
895 0 : return GetColData( iCol, pszDefault );
896 : }
897 :
898 : /************************************************************************/
899 : /* GetColDataLength() */
900 : /************************************************************************/
901 :
902 0 : int CPLODBCStatement::GetColDataLength( int iCol )
903 :
904 : {
905 0 : if( iCol < 0 || iCol >= m_nColCount )
906 0 : return 0;
907 0 : else if( m_papszColValues[iCol] != NULL )
908 0 : return (int)m_panColValueLengths[iCol];
909 : else
910 0 : return 0;
911 : }
912 :
913 : /************************************************************************/
914 : /* GetColId() */
915 : /************************************************************************/
916 :
917 : /**
918 : * Fetch column index.
919 : *
920 : * Gets the column index corresponding with the passed name. The
921 : * name comparisons are case insensitive.
922 : *
923 : * @param pszColName the name to search for.
924 : *
925 : * @return the column index, or -1 if not found.
926 : */
927 :
928 0 : int CPLODBCStatement::GetColId( const char *pszColName )
929 :
930 : {
931 0 : for( SQLSMALLINT iCol = 0; iCol < m_nColCount; iCol++ )
932 0 : if( EQUAL(pszColName, m_papszColNames[iCol]) )
933 0 : return iCol;
934 :
935 0 : return -1;
936 : }
937 :
938 : /************************************************************************/
939 : /* ClearColumnData() */
940 : /************************************************************************/
941 :
942 0 : void CPLODBCStatement::ClearColumnData()
943 :
944 : {
945 0 : if( m_nColCount > 0 )
946 : {
947 0 : for( int iCol = 0; iCol < m_nColCount; iCol++ )
948 : {
949 0 : if( m_papszColValues[iCol] != NULL )
950 : {
951 0 : CPLFree( m_papszColValues[iCol] );
952 0 : m_papszColValues[iCol] = NULL;
953 : }
954 : }
955 : }
956 0 : }
957 :
958 : /************************************************************************/
959 : /* Failed() */
960 : /************************************************************************/
961 :
962 0 : int CPLODBCStatement::Failed( int nResultCode )
963 :
964 : {
965 0 : if( m_poSession != NULL )
966 0 : return m_poSession->Failed( nResultCode, m_hStmt );
967 :
968 0 : return TRUE;
969 : }
970 :
971 : /************************************************************************/
972 : /* Append(const char *) */
973 : /************************************************************************/
974 :
975 : /**
976 : * Append text to internal command.
977 : *
978 : * The passed text is appended to the internal SQL command text.
979 : *
980 : * @param pszText text to append.
981 : */
982 :
983 0 : void CPLODBCStatement::Append( const char *pszText )
984 :
985 : {
986 0 : size_t nTextLen = strlen(pszText);
987 :
988 0 : if( m_nStatementMax < m_nStatementLen + nTextLen + 1 )
989 : {
990 0 : m_nStatementMax = (m_nStatementLen + nTextLen) * 2 + 100;
991 0 : if( m_pszStatement == NULL )
992 : {
993 0 : m_pszStatement = (char *) VSIMalloc(m_nStatementMax);
994 0 : m_pszStatement[0] = '\0';
995 : }
996 : else
997 : {
998 0 : m_pszStatement = (char *) VSIRealloc(m_pszStatement, m_nStatementMax);
999 : }
1000 : }
1001 :
1002 0 : strcpy( m_pszStatement + m_nStatementLen, pszText );
1003 0 : m_nStatementLen += nTextLen;
1004 0 : }
1005 :
1006 : /************************************************************************/
1007 : /* AppendEscaped(const char *) */
1008 : /************************************************************************/
1009 :
1010 : /**
1011 : * Append text to internal command.
1012 : *
1013 : * The passed text is appended to the internal SQL command text after
1014 : * escaping any special characters so it can be used as a character string
1015 : * in an SQL statement.
1016 : *
1017 : * @param pszText text to append.
1018 : */
1019 :
1020 0 : void CPLODBCStatement::AppendEscaped( const char *pszText )
1021 :
1022 : {
1023 0 : size_t iIn, iOut ,nTextLen = strlen(pszText);
1024 0 : char *pszEscapedText = (char *) VSIMalloc(nTextLen*2 + 1);
1025 :
1026 0 : for( iIn = 0, iOut = 0; iIn < nTextLen; iIn++ )
1027 : {
1028 0 : switch( pszText[iIn] )
1029 : {
1030 : case '\'':
1031 : case '\\':
1032 0 : pszEscapedText[iOut++] = '\\';
1033 0 : pszEscapedText[iOut++] = pszText[iIn];
1034 0 : break;
1035 :
1036 : default:
1037 0 : pszEscapedText[iOut++] = pszText[iIn];
1038 : break;
1039 : }
1040 : }
1041 :
1042 0 : pszEscapedText[iOut] = '\0';
1043 :
1044 0 : Append( pszEscapedText );
1045 0 : CPLFree( pszEscapedText );
1046 0 : }
1047 :
1048 : /************************************************************************/
1049 : /* Append(int) */
1050 : /************************************************************************/
1051 :
1052 : /**
1053 : * Append to internal command.
1054 : *
1055 : * The passed value is formatted and appended to the internal SQL command text.
1056 : *
1057 : * @param nValue value to append to the command.
1058 : */
1059 :
1060 0 : void CPLODBCStatement::Append( int nValue )
1061 :
1062 : {
1063 : char szFormattedValue[100];
1064 :
1065 0 : sprintf( szFormattedValue, "%d", nValue );
1066 0 : Append( szFormattedValue );
1067 0 : }
1068 :
1069 : /************************************************************************/
1070 : /* Append(double) */
1071 : /************************************************************************/
1072 :
1073 : /**
1074 : * Append to internal command.
1075 : *
1076 : * The passed value is formatted and appended to the internal SQL command text.
1077 : *
1078 : * @param dfValue value to append to the command.
1079 : */
1080 :
1081 0 : void CPLODBCStatement::Append( double dfValue )
1082 :
1083 : {
1084 : char szFormattedValue[100];
1085 :
1086 0 : sprintf( szFormattedValue, "%24g", dfValue );
1087 0 : Append( szFormattedValue );
1088 0 : }
1089 :
1090 : /************************************************************************/
1091 : /* Appendf() */
1092 : /************************************************************************/
1093 :
1094 : /**
1095 : * Append to internal command.
1096 : *
1097 : * The passed format is used to format other arguments and the result is
1098 : * appended to the internal command text. Long results may not be formatted
1099 : * properly, and should be appended with the direct Append() methods.
1100 : *
1101 : * @param pszFormat printf() style format string.
1102 : *
1103 : * @return FALSE if formatting fails dueto result being too large.
1104 : */
1105 :
1106 0 : int CPLODBCStatement::Appendf( const char *pszFormat, ... )
1107 :
1108 : {
1109 : va_list args;
1110 : char szFormattedText[8000];
1111 : int bSuccess;
1112 :
1113 0 : va_start( args, pszFormat );
1114 : #if defined(HAVE_VSNPRINTF)
1115 : bSuccess = vsnprintf( szFormattedText, sizeof(szFormattedText)-1,
1116 0 : pszFormat, args ) < (int) sizeof(szFormattedText)-1;
1117 : #else
1118 : vsprintf( szFormattedText, pszFormat, args );
1119 : bSuccess = TRUE;
1120 : #endif
1121 0 : va_end( args );
1122 :
1123 0 : if( bSuccess )
1124 0 : Append( szFormattedText );
1125 :
1126 0 : return bSuccess;
1127 : }
1128 :
1129 : /************************************************************************/
1130 : /* Clear() */
1131 : /************************************************************************/
1132 :
1133 : /**
1134 : * Clear internal command text and result set definitions.
1135 : */
1136 :
1137 0 : void CPLODBCStatement::Clear()
1138 :
1139 : {
1140 0 : ClearColumnData();
1141 :
1142 0 : if( m_pszStatement != NULL )
1143 : {
1144 0 : CPLFree( m_pszStatement );
1145 0 : m_pszStatement = NULL;
1146 : }
1147 :
1148 0 : m_nStatementLen = 0;
1149 0 : m_nStatementMax = 0;
1150 :
1151 0 : if( m_papszColNames )
1152 : {
1153 0 : CPLFree( m_panColType );
1154 0 : m_panColType = NULL;
1155 :
1156 0 : CSLDestroy( m_papszColTypeNames );
1157 0 : m_papszColTypeNames = NULL;
1158 :
1159 0 : CPLFree( m_panColSize );
1160 0 : m_panColSize = NULL;
1161 :
1162 0 : CPLFree( m_panColPrecision );
1163 0 : m_panColPrecision = NULL;
1164 :
1165 0 : CPLFree( m_panColNullable );
1166 0 : m_panColNullable = NULL;
1167 :
1168 0 : CSLDestroy( m_papszColNames );
1169 0 : m_papszColNames = NULL;
1170 :
1171 0 : CPLFree( m_papszColValues );
1172 0 : m_papszColValues = NULL;
1173 :
1174 0 : CPLFree( m_panColValueLengths );
1175 0 : m_panColValueLengths = NULL;
1176 : }
1177 :
1178 0 : }
1179 :
1180 : /************************************************************************/
1181 : /* GetColumns() */
1182 : /************************************************************************/
1183 :
1184 : /**
1185 : * Fetch column definitions for a table.
1186 : *
1187 : * The SQLColumn() method is used to fetch the definitions for the columns
1188 : * of a table (or other queriable object such as a view). The column
1189 : * definitions are digested and used to populate the CPLODBCStatement
1190 : * column definitions essentially as if a "SELECT * FROM tablename" had
1191 : * been done; however, no resultset will be available.
1192 : *
1193 : * @param pszTable the name of the table to query information on. This
1194 : * should not be empty.
1195 : *
1196 : * @param pszCatalog the catalog to find the table in, use NULL (the
1197 : * default) if no catalog is available.
1198 : *
1199 : * @param pszSchema the schema to find the table in, use NULL (the
1200 : * default) if no schema is available.
1201 : *
1202 : * @return TRUE on success or FALSE on failure.
1203 : */
1204 :
1205 : int CPLODBCStatement::GetColumns( const char *pszTable,
1206 : const char *pszCatalog,
1207 0 : const char *pszSchema )
1208 :
1209 : {
1210 : #ifdef notdef
1211 : if( pszCatalog == NULL )
1212 : pszCatalog = "";
1213 : if( pszSchema == NULL )
1214 : pszSchema = "";
1215 : #endif
1216 : /* -------------------------------------------------------------------- */
1217 : /* Fetch columns resultset for this table. */
1218 : /* -------------------------------------------------------------------- */
1219 0 : if( Failed( SQLColumns( m_hStmt,
1220 : (SQLCHAR *) pszCatalog, SQL_NTS,
1221 : (SQLCHAR *) pszSchema, SQL_NTS,
1222 : (SQLCHAR *) pszTable, SQL_NTS,
1223 : (SQLCHAR *) NULL /* "" */, SQL_NTS ) ) )
1224 0 : return FALSE;
1225 :
1226 : /* -------------------------------------------------------------------- */
1227 : /* Allocate per column information. */
1228 : /* -------------------------------------------------------------------- */
1229 : #ifdef notdef
1230 : // SQLRowCount() is too unreliable (with unixodbc on AIX for instance)
1231 : // so we now avoid it.
1232 : SQLINTEGER nResultCount=0;
1233 :
1234 : if( Failed(SQLRowCount( m_hStmt, &nResultCount ) ) )
1235 : nResultCount = 0;
1236 :
1237 : if( nResultCount < 1 )
1238 : m_nColCount = 500; // Hopefully lots.
1239 : else
1240 : m_nColCount = nResultCount;
1241 : #endif
1242 :
1243 0 : m_nColCount = 500;
1244 :
1245 0 : m_papszColNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
1246 0 : m_papszColValues = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
1247 :
1248 0 : m_panColType = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
1249 0 : m_papszColTypeNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1));
1250 0 : m_panColSize = (_SQLULEN *) CPLCalloc(sizeof(_SQLULEN),m_nColCount);
1251 0 : m_panColPrecision = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
1252 0 : m_panColNullable = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount);
1253 :
1254 : /* -------------------------------------------------------------------- */
1255 : /* Establish columns to use for key information. */
1256 : /* -------------------------------------------------------------------- */
1257 : SQLUSMALLINT iCol;
1258 :
1259 0 : for( iCol = 0; iCol < m_nColCount; iCol++ )
1260 : {
1261 : char szWrkData[8193];
1262 : _SQLLEN cbDataLen;
1263 :
1264 0 : if( Failed( SQLFetch( m_hStmt ) ) )
1265 : {
1266 0 : m_nColCount = iCol;
1267 0 : break;
1268 : }
1269 :
1270 0 : szWrkData[0] = '\0';
1271 :
1272 : SQLGetData( m_hStmt, SQLColumns_COLUMN_NAME, SQL_C_CHAR,
1273 0 : szWrkData, sizeof(szWrkData)-1, &cbDataLen );
1274 0 : m_papszColNames[iCol] = CPLStrdup(szWrkData);
1275 :
1276 : SQLGetData( m_hStmt, SQLColumns_DATA_TYPE, SQL_C_CHAR,
1277 0 : szWrkData, sizeof(szWrkData)-1, &cbDataLen );
1278 0 : m_panColType[iCol] = (short) atoi(szWrkData);
1279 :
1280 : SQLGetData( m_hStmt, SQLColumns_TYPE_NAME, SQL_C_CHAR,
1281 0 : szWrkData, sizeof(szWrkData)-1, &cbDataLen );
1282 0 : m_papszColTypeNames[iCol] = CPLStrdup(szWrkData);
1283 :
1284 : SQLGetData( m_hStmt, SQLColumns_COLUMN_SIZE, SQL_C_CHAR,
1285 0 : szWrkData, sizeof(szWrkData)-1, &cbDataLen );
1286 0 : m_panColSize[iCol] = atoi(szWrkData);
1287 :
1288 : SQLGetData( m_hStmt, SQLColumns_DECIMAL_DIGITS, SQL_C_CHAR,
1289 0 : szWrkData, sizeof(szWrkData)-1, &cbDataLen );
1290 0 : m_panColPrecision[iCol] = (short) atoi(szWrkData);
1291 :
1292 : SQLGetData( m_hStmt, SQLColumns_NULLABLE, SQL_C_CHAR,
1293 0 : szWrkData, sizeof(szWrkData)-1, &cbDataLen );
1294 0 : m_panColNullable[iCol] = atoi(szWrkData) == SQL_NULLABLE;
1295 : }
1296 :
1297 0 : return TRUE;
1298 : }
1299 :
1300 : /************************************************************************/
1301 : /* GetPrimaryKeys() */
1302 : /************************************************************************/
1303 :
1304 : /**
1305 : * Fetch primary keys for a table.
1306 : *
1307 : * The SQLPrimaryKeys() function is used to fetch a list of fields
1308 : * forming the primary key. The result is returned as a result set matching
1309 : * the SQLPrimaryKeys() function result set. The 4th column in the result
1310 : * set is the column name of the key, and if the result set contains only
1311 : * one record then that single field will be the complete primary key.
1312 : *
1313 : * @param pszTable the name of the table to query information on. This
1314 : * should not be empty.
1315 : *
1316 : * @param pszCatalog the catalog to find the table in, use NULL (the
1317 : * default) if no catalog is available.
1318 : *
1319 : * @param pszSchema the schema to find the table in, use NULL (the
1320 : * default) if no schema is available.
1321 : *
1322 : * @return TRUE on success or FALSE on failure.
1323 : */
1324 :
1325 : int CPLODBCStatement::GetPrimaryKeys( const char *pszTable,
1326 : const char *pszCatalog,
1327 0 : const char *pszSchema )
1328 :
1329 : {
1330 0 : if( pszCatalog == NULL )
1331 0 : pszCatalog = "";
1332 0 : if( pszSchema == NULL )
1333 0 : pszSchema = "";
1334 :
1335 : /* -------------------------------------------------------------------- */
1336 : /* Fetch columns resultset for this table. */
1337 : /* -------------------------------------------------------------------- */
1338 0 : if( Failed( SQLPrimaryKeys( m_hStmt,
1339 : (SQLCHAR *) pszCatalog, SQL_NTS,
1340 : (SQLCHAR *) pszSchema, SQL_NTS,
1341 : (SQLCHAR *) pszTable, SQL_NTS ) ) )
1342 0 : return FALSE;
1343 : else
1344 0 : return CollectResultsInfo();
1345 : }
1346 :
1347 : /************************************************************************/
1348 : /* GetTables() */
1349 : /************************************************************************/
1350 :
1351 : /**
1352 : * Fetch tables in database.
1353 : *
1354 : * The SQLTables() function is used to fetch a list tables in the
1355 : * database. The result is returned as a result set matching
1356 : * the SQLTables() function result set. The 3rd column in the result
1357 : * set is the table name. Only tables of type "TABLE" are returned.
1358 : *
1359 : * @param pszCatalog the catalog to find the table in, use NULL (the
1360 : * default) if no catalog is available.
1361 : *
1362 : * @param pszSchema the schema to find the table in, use NULL (the
1363 : * default) if no schema is available.
1364 : *
1365 : * @return TRUE on success or FALSE on failure.
1366 : */
1367 :
1368 : int CPLODBCStatement::GetTables( const char *pszCatalog,
1369 0 : const char *pszSchema )
1370 :
1371 : {
1372 : CPLDebug( "ODBC", "CatalogNameL: %s\nSchema name: %s\n",
1373 0 : pszCatalog, pszSchema );
1374 :
1375 : /* -------------------------------------------------------------------- */
1376 : /* Fetch columns resultset for this table. */
1377 : /* -------------------------------------------------------------------- */
1378 0 : if( Failed( SQLTables( m_hStmt,
1379 : (SQLCHAR *) pszCatalog, SQL_NTS,
1380 : (SQLCHAR *) pszSchema, SQL_NTS,
1381 : (SQLCHAR *) NULL, SQL_NTS,
1382 : (SQLCHAR *) "'TABLE','VIEW'", SQL_NTS ) ) )
1383 0 : return FALSE;
1384 : else
1385 0 : return CollectResultsInfo();
1386 : }
1387 :
1388 : /************************************************************************/
1389 : /* DumpResult() */
1390 : /************************************************************************/
1391 :
1392 : /**
1393 : * Dump resultset to file.
1394 : *
1395 : * The contents of the current resultset are dumped in a simply formatted
1396 : * form to the provided file. If requested, the schema definition will
1397 : * be written first.
1398 : *
1399 : * @param fp the file to write to. stdout or stderr are acceptable.
1400 : *
1401 : * @param bShowSchema TRUE to force writing schema information for the rowset
1402 : * before the rowset data itself. Default is FALSE.
1403 : */
1404 :
1405 0 : void CPLODBCStatement::DumpResult( FILE *fp, int bShowSchema )
1406 :
1407 : {
1408 : int iCol;
1409 :
1410 : /* -------------------------------------------------------------------- */
1411 : /* Display schema */
1412 : /* -------------------------------------------------------------------- */
1413 0 : if( bShowSchema )
1414 : {
1415 0 : fprintf( fp, "Column Definitions:\n" );
1416 0 : for( iCol = 0; iCol < GetColCount(); iCol++ )
1417 : {
1418 0 : fprintf( fp, " %2d: %-24s ", iCol, GetColName(iCol) );
1419 0 : if( GetColPrecision(iCol) > 0
1420 : && GetColPrecision(iCol) != GetColSize(iCol) )
1421 : fprintf( fp, " Size:%3d.%d",
1422 0 : GetColSize(iCol), GetColPrecision(iCol) );
1423 : else
1424 0 : fprintf( fp, " Size:%5d", GetColSize(iCol) );
1425 :
1426 0 : CPLString osType = GetTypeName( GetColType(iCol) );
1427 0 : fprintf( fp, " Type:%s", osType.c_str() );
1428 0 : if( GetColNullable(iCol) )
1429 0 : fprintf( fp, " NULLABLE" );
1430 0 : fprintf( fp, "\n" );
1431 : }
1432 0 : fprintf( fp, "\n" );
1433 : }
1434 :
1435 : /* -------------------------------------------------------------------- */
1436 : /* Display results */
1437 : /* -------------------------------------------------------------------- */
1438 0 : int iRecord = 0;
1439 0 : while( Fetch() )
1440 : {
1441 0 : fprintf( fp, "Record %d\n", iRecord++ );
1442 :
1443 0 : for( iCol = 0; iCol < GetColCount(); iCol++ )
1444 : {
1445 0 : fprintf( fp, " %s: %s\n", GetColName(iCol), GetColData(iCol) );
1446 : }
1447 : }
1448 0 : }
1449 :
1450 : /************************************************************************/
1451 : /* GetTypeName() */
1452 : /************************************************************************/
1453 :
1454 : /**
1455 : * Get name for SQL column type.
1456 : *
1457 : * Returns a string name for the indicated type code (as returned
1458 : * from CPLODBCStatement::GetColType()).
1459 : *
1460 : * @param nTypeCode the SQL_ code, such as SQL_CHAR.
1461 : *
1462 : * @return internal string, "UNKNOWN" if code not recognised.
1463 : */
1464 :
1465 0 : CPLString CPLODBCStatement::GetTypeName( int nTypeCode )
1466 :
1467 : {
1468 0 : switch( nTypeCode )
1469 : {
1470 : case SQL_CHAR:
1471 0 : return "CHAR";
1472 :
1473 : case SQL_NUMERIC:
1474 0 : return "NUMERIC";
1475 :
1476 : case SQL_DECIMAL:
1477 0 : return "DECIMAL";
1478 :
1479 : case SQL_INTEGER:
1480 0 : return "INTEGER";
1481 :
1482 : case SQL_SMALLINT:
1483 0 : return "SMALLINT";
1484 :
1485 :
1486 : case SQL_FLOAT:
1487 0 : return "FLOAT";
1488 :
1489 : case SQL_REAL:
1490 0 : return "REAL";
1491 :
1492 : case SQL_DOUBLE:
1493 0 : return "DOUBLE";
1494 :
1495 : case SQL_DATETIME:
1496 0 : return "DATETIME";
1497 :
1498 : case SQL_VARCHAR:
1499 0 : return "VARCHAR";
1500 :
1501 : case SQL_TYPE_DATE:
1502 0 : return "DATE";
1503 :
1504 : case SQL_TYPE_TIME:
1505 0 : return "TIME";
1506 :
1507 : case SQL_TYPE_TIMESTAMP:
1508 0 : return "TIMESTAMP";
1509 :
1510 : default:
1511 0 : CPLString osResult;
1512 0 : osResult.Printf( "UNKNOWN:%d", nTypeCode );
1513 0 : return osResult;
1514 : }
1515 : }
1516 :
1517 : /************************************************************************/
1518 : /* GetTypeMapping() */
1519 : /************************************************************************/
1520 :
1521 : /**
1522 : * Get appropriate C data type for SQL column type.
1523 : *
1524 : * Returns a C data type code, corresponding to the indicated SQL data
1525 : * type code (as returned from CPLODBCStatement::GetColType()).
1526 : *
1527 : * @param nTypeCode the SQL_ code, such as SQL_CHAR.
1528 : *
1529 : * @return data type code. The valid code is always returned. If SQL
1530 : * code is not recognised, SQL_C_BINARY will be returned.
1531 : */
1532 :
1533 0 : SQLSMALLINT CPLODBCStatement::GetTypeMapping( SQLSMALLINT nTypeCode )
1534 :
1535 : {
1536 0 : switch( nTypeCode )
1537 : {
1538 : case SQL_CHAR:
1539 : case SQL_VARCHAR:
1540 : case SQL_LONGVARCHAR:
1541 0 : return SQL_C_CHAR;
1542 :
1543 : case SQL_WCHAR:
1544 : case SQL_WVARCHAR:
1545 : case SQL_WLONGVARCHAR:
1546 0 : return SQL_C_WCHAR;
1547 :
1548 : case SQL_DECIMAL:
1549 : case SQL_NUMERIC:
1550 0 : return SQL_C_NUMERIC;
1551 :
1552 : case SQL_SMALLINT:
1553 0 : return SQL_C_SSHORT;
1554 :
1555 : case SQL_INTEGER:
1556 0 : return SQL_C_SLONG;
1557 :
1558 : case SQL_REAL:
1559 0 : return SQL_C_FLOAT;
1560 :
1561 : case SQL_FLOAT:
1562 : case SQL_DOUBLE:
1563 0 : return SQL_C_DOUBLE;
1564 :
1565 : case SQL_BIT:
1566 : case SQL_TINYINT:
1567 : case SQL_BIGINT:
1568 : /* case SQL_TYPE_UTCDATETIME:
1569 : case SQL_TYPE_UTCTIME:*/
1570 : case SQL_INTERVAL_MONTH:
1571 : case SQL_INTERVAL_YEAR:
1572 : case SQL_INTERVAL_YEAR_TO_MONTH:
1573 : case SQL_INTERVAL_DAY:
1574 : case SQL_INTERVAL_HOUR:
1575 : case SQL_INTERVAL_MINUTE:
1576 : case SQL_INTERVAL_SECOND:
1577 : case SQL_INTERVAL_DAY_TO_HOUR:
1578 : case SQL_INTERVAL_DAY_TO_MINUTE:
1579 : case SQL_INTERVAL_DAY_TO_SECOND:
1580 : case SQL_INTERVAL_HOUR_TO_MINUTE:
1581 : case SQL_INTERVAL_HOUR_TO_SECOND:
1582 : case SQL_INTERVAL_MINUTE_TO_SECOND:
1583 : case SQL_GUID:
1584 0 : return SQL_C_CHAR;
1585 :
1586 : case SQL_DATE:
1587 : case SQL_TYPE_DATE:
1588 0 : return SQL_C_DATE;
1589 :
1590 : case SQL_TIME:
1591 : case SQL_TYPE_TIME:
1592 0 : return SQL_C_TIME;
1593 :
1594 : case SQL_TIMESTAMP:
1595 : case SQL_TYPE_TIMESTAMP:
1596 0 : return SQL_C_TIMESTAMP;
1597 :
1598 : case SQL_BINARY:
1599 : case SQL_VARBINARY:
1600 : case SQL_LONGVARBINARY:
1601 0 : return SQL_C_BINARY;
1602 :
1603 : default:
1604 0 : return SQL_C_CHAR;
1605 : }
1606 : }
1607 :
1608 : #endif /* #ifndef WIN32CE */
|