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