Back to index

openldap  2.4.31
sql-wrap.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1999-2012 The OpenLDAP Foundation.
00005  * Portions Copyright 1999 Dmitry Kovalev.
00006  * Portions Copyright 2002 Pierangelo Masarati.
00007  * Portions Copyright 2004 Mark Adamson.
00008  * All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without
00011  * modification, are permitted only as authorized by the OpenLDAP
00012  * Public License.
00013  *
00014  * A copy of this license is available in the file LICENSE in the
00015  * top-level directory of the distribution or, alternatively, at
00016  * <http://www.OpenLDAP.org/license.html>.
00017  */
00018 /* ACKNOWLEDGEMENTS:
00019  * This work was initially developed by Dmitry Kovalev for inclusion
00020  * by OpenLDAP Software.  Additional significant contributors include
00021  * Pierangelo Masarati and Mark Adamson.
00022  */
00023 
00024 #include "portable.h"
00025 
00026 #include <stdio.h>
00027 #include "ac/string.h"
00028 #include <sys/types.h>
00029 
00030 #include "slap.h"
00031 #include "proto-sql.h"
00032 
00033 #define MAX_ATTR_LEN 16384
00034 
00035 void
00036 backsql_PrintErrors( SQLHENV henv, SQLHDBC hdbc, SQLHSTMT sth, int rc )
00037 {
00038        SQLCHAR       msg[SQL_MAX_MESSAGE_LENGTH];              /* msg. buffer    */
00039        SQLCHAR       state[SQL_SQLSTATE_SIZE];          /* statement buf. */
00040        SDWORD iSqlCode;                          /* return code    */
00041        SWORD  len = SQL_MAX_MESSAGE_LENGTH - 1;  /* return length  */ 
00042 
00043        Debug( LDAP_DEBUG_TRACE, "Return code: %d\n", rc, 0, 0 );
00044 
00045        for ( ; rc = SQLError( henv, hdbc, sth, state, &iSqlCode, msg,
00046               SQL_MAX_MESSAGE_LENGTH - 1, &len ), BACKSQL_SUCCESS( rc ); )
00047        {
00048               Debug( LDAP_DEBUG_TRACE,
00049                      "   nativeErrCode=%d SQLengineState=%s msg=\"%s\"\n",
00050                      (int)iSqlCode, state, msg );
00051        }
00052 }
00053 
00054 RETCODE
00055 backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, const char *query, int timeout )
00056 {
00057        RETCODE              rc;
00058 
00059        rc = SQLAllocStmt( dbh, sth );
00060        if ( rc != SQL_SUCCESS ) {
00061               return rc;
00062        }
00063 
00064 #ifdef BACKSQL_TRACE
00065        Debug( LDAP_DEBUG_TRACE, "==>backsql_Prepare()\n", 0, 0, 0 );
00066 #endif /* BACKSQL_TRACE */
00067 
00068 #ifdef BACKSQL_MSSQL_WORKAROUND
00069        {
00070               char          drv_name[ 30 ];
00071               SWORD         len;
00072 
00073               SQLGetInfo( dbh, SQL_DRIVER_NAME, drv_name, sizeof( drv_name ), &len );
00074 
00075 #ifdef BACKSQL_TRACE
00076               Debug( LDAP_DEBUG_TRACE, "backsql_Prepare(): driver name=\"%s\"\n",
00077                             drv_name, 0, 0 );
00078 #endif /* BACKSQL_TRACE */
00079 
00080               ldap_pvt_str2upper( drv_name );
00081               if ( !strncmp( drv_name, "SQLSRV32.DLL", STRLENOF( "SQLSRV32.DLL" ) ) ) {
00082                      /*
00083                       * stupid default result set in MS SQL Server
00084                       * does not support multiple active statements
00085                       * on the same connection -- so we are trying 
00086                       * to make it not to use default result set...
00087                       */
00088                      Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
00089                             "enabling MS SQL Server default result "
00090                             "set workaround\n", 0, 0, 0 );
00091                      rc = SQLSetStmtOption( *sth, SQL_CONCURRENCY, 
00092                                    SQL_CONCUR_ROWVER );
00093                      if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) {
00094                             Debug( LDAP_DEBUG_TRACE, "backsql_Prepare(): "
00095                                    "SQLSetStmtOption(SQL_CONCURRENCY,"
00096                                    "SQL_CONCUR_ROWVER) failed:\n", 
00097                                    0, 0, 0 );
00098                             backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
00099                             SQLFreeStmt( *sth, SQL_DROP );
00100                             return rc;
00101                      }
00102               }
00103        }
00104 #endif /* BACKSQL_MSSQL_WORKAROUND */
00105 
00106        if ( timeout > 0 ) {
00107               Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
00108                      "setting query timeout to %d sec.\n", 
00109                      timeout, 0, 0 );
00110               rc = SQLSetStmtOption( *sth, SQL_QUERY_TIMEOUT, timeout );
00111               if ( rc != SQL_SUCCESS ) {
00112                      backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
00113                      SQLFreeStmt( *sth, SQL_DROP );
00114                      return rc;
00115               }
00116        }
00117 
00118 #ifdef BACKSQL_TRACE
00119        Debug( LDAP_DEBUG_TRACE, "<==backsql_Prepare() calling SQLPrepare()\n",
00120                      0, 0, 0 );
00121 #endif /* BACKSQL_TRACE */
00122 
00123        return SQLPrepare( *sth, (SQLCHAR *)query, SQL_NTS );
00124 }
00125 
00126 RETCODE
00127 backsql_BindRowAsStrings_x( SQLHSTMT sth, BACKSQL_ROW_NTS *row, void *ctx )
00128 {
00129        RETCODE              rc;
00130 
00131        if ( row == NULL ) {
00132               return SQL_ERROR;
00133        }
00134 
00135 #ifdef BACKSQL_TRACE
00136        Debug( LDAP_DEBUG_TRACE, "==> backsql_BindRowAsStrings()\n", 0, 0, 0 );
00137 #endif /* BACKSQL_TRACE */
00138        
00139        rc = SQLNumResultCols( sth, &row->ncols );
00140        if ( rc != SQL_SUCCESS ) {
00141 #ifdef BACKSQL_TRACE
00142               Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings(): "
00143                      "SQLNumResultCols() failed:\n", 0, 0, 0 );
00144 #endif /* BACKSQL_TRACE */
00145               
00146               backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC, sth, rc );
00147 
00148        } else {
00149               SQLCHAR              colname[ 64 ];
00150               SQLSMALLINT   name_len, col_type, col_scale, col_null;
00151               SQLLEN        col_prec;
00152               int           i;
00153 
00154 #ifdef BACKSQL_TRACE
00155               Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
00156                      "ncols=%d\n", (int)row->ncols, 0, 0 );
00157 #endif /* BACKSQL_TRACE */
00158 
00159               row->col_names = (BerVarray)ber_memcalloc_x( row->ncols + 1, 
00160                             sizeof( struct berval ), ctx );
00161               if ( row->col_names == NULL ) {
00162                      goto nomem;
00163               }
00164 
00165               row->col_prec = (UDWORD *)ber_memcalloc_x( row->ncols,
00166                             sizeof( UDWORD ), ctx );
00167               if ( row->col_prec == NULL ) {
00168                      goto nomem;
00169               }
00170 
00171               row->col_type = (SQLSMALLINT *)ber_memcalloc_x( row->ncols,
00172                             sizeof( SQLSMALLINT ), ctx );
00173               if ( row->col_type == NULL ) {
00174                      goto nomem;
00175               }
00176 
00177               row->cols = (char **)ber_memcalloc_x( row->ncols + 1, 
00178                             sizeof( char * ), ctx );
00179               if ( row->cols == NULL ) {
00180                      goto nomem;
00181               }
00182 
00183               row->value_len = (SQLLEN *)ber_memcalloc_x( row->ncols,
00184                             sizeof( SQLLEN ), ctx );
00185               if ( row->value_len == NULL ) {
00186                      goto nomem;
00187               }
00188 
00189               if ( 0 ) {
00190 nomem:
00191                      ber_memfree_x( row->col_names, ctx );
00192                      row->col_names = NULL;
00193                      ber_memfree_x( row->col_prec, ctx );
00194                      row->col_prec = NULL;
00195                      ber_memfree_x( row->col_type, ctx );
00196                      row->col_type = NULL;
00197                      ber_memfree_x( row->cols, ctx );
00198                      row->cols = NULL;
00199                      ber_memfree_x( row->value_len, ctx );
00200                      row->value_len = NULL;
00201 
00202                      Debug( LDAP_DEBUG_ANY, "backsql_BindRowAsStrings: "
00203                             "out of memory\n", 0, 0, 0 );
00204 
00205                      return LDAP_NO_MEMORY;
00206               }
00207 
00208               for ( i = 0; i < row->ncols; i++ ) {
00209                      SQLSMALLINT   TargetType;
00210 
00211                      rc = SQLDescribeCol( sth, (SQLSMALLINT)(i + 1), &colname[ 0 ],
00212                                    (SQLUINTEGER)( sizeof( colname ) - 1 ),
00213                                    &name_len, &col_type,
00214                                    &col_prec, &col_scale, &col_null );
00215                      /* FIXME: test rc? */
00216 
00217                      ber_str2bv_x( (char *)colname, 0, 1,
00218                                    &row->col_names[ i ], ctx );
00219 #ifdef BACKSQL_TRACE
00220                      Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
00221                             "col_name=%s, col_prec[%d]=%d\n",
00222                             colname, (int)(i + 1), (int)col_prec );
00223 #endif /* BACKSQL_TRACE */
00224                      if ( col_type != SQL_CHAR && col_type != SQL_VARCHAR )
00225                      {
00226                             col_prec = MAX_ATTR_LEN;
00227                      }
00228 
00229                      row->cols[ i ] = (char *)ber_memcalloc_x( col_prec + 1,
00230                                    sizeof( char ), ctx );
00231                      row->col_prec[ i ] = col_prec;
00232                      row->col_type[ i ] = col_type;
00233 
00234                      /*
00235                       * ITS#3386, ITS#3113 - 20070308
00236                       * Note: there are many differences between various DPMS and ODBC
00237                       * Systems; some support SQL_C_BLOB, SQL_C_BLOB_LOCATOR.  YMMV:
00238                       * This has only been tested on Linux/MySQL/UnixODBC
00239                       * For BINARY-type Fields (BLOB, etc), read the data as BINARY
00240                       */
00241                      if ( BACKSQL_IS_BINARY( col_type ) ) {
00242 #ifdef BACKSQL_TRACE
00243                             Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
00244                                    "col_name=%s, col_type[%d]=%d: reading binary data\n",
00245                                    colname, (int)(i + 1), (int)col_type);
00246 #endif /* BACKSQL_TRACE */
00247                             TargetType = SQL_C_BINARY;
00248 
00249                      } else {
00250                             /* Otherwise read it as Character data */
00251 #ifdef BACKSQL_TRACE
00252                             Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
00253                                    "col_name=%s, col_type[%d]=%d: reading character data\n",
00254                                    colname, (int)(i + 1), (int)col_type);
00255 #endif /* BACKSQL_TRACE */
00256                             TargetType = SQL_C_CHAR;
00257                      }
00258 
00259                      rc = SQLBindCol( sth, (SQLUSMALLINT)(i + 1),
00260                              TargetType,
00261                              (SQLPOINTER)row->cols[ i ],
00262                              col_prec + 1,
00263                              &row->value_len[ i ] );
00264 
00265                      /* FIXME: test rc? */
00266               }
00267 
00268               BER_BVZERO( &row->col_names[ i ] );
00269               row->cols[ i ] = NULL;
00270        }
00271 
00272 #ifdef BACKSQL_TRACE
00273        Debug( LDAP_DEBUG_TRACE, "<== backsql_BindRowAsStrings()\n", 0, 0, 0 );
00274 #endif /* BACKSQL_TRACE */
00275 
00276        return rc;
00277 }
00278 
00279 RETCODE
00280 backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row )
00281 {
00282        return backsql_BindRowAsStrings_x( sth, row, NULL );
00283 }
00284 
00285 RETCODE
00286 backsql_FreeRow_x( BACKSQL_ROW_NTS *row, void *ctx )
00287 {
00288        if ( row->cols == NULL ) {
00289               return SQL_ERROR;
00290        }
00291 
00292        ber_bvarray_free_x( row->col_names, ctx );
00293        ber_memfree_x( row->col_prec, ctx );
00294        ber_memfree_x( row->col_type, ctx );
00295        ber_memvfree_x( (void **)row->cols, ctx );
00296        ber_memfree_x( row->value_len, ctx );
00297 
00298        return SQL_SUCCESS;
00299 }
00300 
00301 
00302 RETCODE
00303 backsql_FreeRow( BACKSQL_ROW_NTS *row )
00304 {
00305        return backsql_FreeRow_x( row, NULL );
00306 }
00307 
00308 static void
00309 backsql_close_db_handle( SQLHDBC dbh )
00310 {
00311        if ( dbh == SQL_NULL_HDBC ) {
00312               return;
00313        }
00314 
00315        Debug( LDAP_DEBUG_TRACE, "==>backsql_close_db_handle(%p)\n",
00316               (void *)dbh, 0, 0 );
00317 
00318        /*
00319         * Default transact is SQL_ROLLBACK; commit is required only
00320         * by write operations, and it is explicitly performed after
00321         * each atomic operation succeeds.
00322         */
00323 
00324        /* TimesTen */
00325        SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
00326        SQLDisconnect( dbh );
00327        SQLFreeConnect( dbh );
00328 
00329        Debug( LDAP_DEBUG_TRACE, "<==backsql_close_db_handle(%p)\n",
00330               (void *)dbh, 0, 0 );
00331 }
00332 
00333 int
00334 backsql_conn_destroy(
00335        backsql_info  *bi )
00336 {
00337        return 0;
00338 }
00339 
00340 int
00341 backsql_init_db_env( backsql_info *bi )
00342 {
00343        RETCODE              rc;
00344        int           ret = SQL_SUCCESS;
00345        
00346        Debug( LDAP_DEBUG_TRACE, "==>backsql_init_db_env()\n", 0, 0, 0 );
00347 
00348        rc = SQLAllocEnv( &bi->sql_db_env );
00349        if ( rc != SQL_SUCCESS ) {
00350               Debug( LDAP_DEBUG_TRACE, "init_db_env: SQLAllocEnv failed:\n",
00351                             0, 0, 0 );
00352               backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC,
00353                             SQL_NULL_HENV, rc );
00354               ret = SQL_ERROR;
00355        }
00356 
00357        Debug( LDAP_DEBUG_TRACE, "<==backsql_init_db_env()=%d\n", ret, 0, 0 );
00358 
00359        return ret;
00360 }
00361 
00362 int
00363 backsql_free_db_env( backsql_info *bi )
00364 {
00365        Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_env()\n", 0, 0, 0 );
00366 
00367        (void)SQLFreeEnv( bi->sql_db_env );
00368        bi->sql_db_env = SQL_NULL_HENV;
00369 
00370        /*
00371         * stop, if frontend waits for all threads to shutdown 
00372         * before calling this -- then what are we going to delete?? 
00373         * everything is already deleted...
00374         */
00375        Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_env()\n", 0, 0, 0 );
00376 
00377        return SQL_SUCCESS;
00378 }
00379 
00380 static int
00381 backsql_open_db_handle(
00382        backsql_info  *bi,
00383        SQLHDBC              *dbhp )
00384 {
00385        /* TimesTen */
00386        char                 DBMSName[ 32 ];
00387        int                  rc;
00388 
00389        assert( dbhp != NULL );
00390        *dbhp = SQL_NULL_HDBC;
00391  
00392        Debug( LDAP_DEBUG_TRACE, "==>backsql_open_db_handle()\n",
00393               0, 0, 0 );
00394 
00395        rc = SQLAllocConnect( bi->sql_db_env, dbhp );
00396        if ( !BACKSQL_SUCCESS( rc ) ) {
00397               Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
00398                      "SQLAllocConnect() failed:\n",
00399                      0, 0, 0 );
00400               backsql_PrintErrors( bi->sql_db_env, SQL_NULL_HDBC,
00401                      SQL_NULL_HENV, rc );
00402               return LDAP_UNAVAILABLE;
00403        }
00404 
00405        rc = SQLConnect( *dbhp,
00406               (SQLCHAR*)bi->sql_dbname, SQL_NTS,
00407               (SQLCHAR*)bi->sql_dbuser, SQL_NTS,
00408               (SQLCHAR*)bi->sql_dbpasswd, SQL_NTS );
00409        if ( rc != SQL_SUCCESS ) {
00410               Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
00411                      "SQLConnect() to database \"%s\" %s.\n",
00412                      bi->sql_dbname,
00413                      rc == SQL_SUCCESS_WITH_INFO ?
00414                             "succeeded with info" : "failed",
00415                      0 );
00416               backsql_PrintErrors( bi->sql_db_env, *dbhp, SQL_NULL_HENV, rc );
00417               if ( rc != SQL_SUCCESS_WITH_INFO ) {
00418                      SQLFreeConnect( *dbhp );
00419                      return LDAP_UNAVAILABLE;
00420               }
00421        }
00422 
00423        /* 
00424         * TimesTen : Turn off autocommit.  We must explicitly
00425         * commit any transactions. 
00426         */
00427        SQLSetConnectOption( *dbhp, SQL_AUTOCOMMIT,
00428               BACKSQL_AUTOCOMMIT_ON( bi ) ?  SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF );
00429 
00430        /* 
00431         * See if this connection is to TimesTen.  If it is,
00432         * remember that fact for later use.
00433         */
00434        /* Assume until proven otherwise */
00435        bi->sql_flags &= ~BSQLF_USE_REVERSE_DN;
00436        DBMSName[ 0 ] = '\0';
00437        rc = SQLGetInfo( *dbhp, SQL_DBMS_NAME, (PTR)&DBMSName,
00438                      sizeof( DBMSName ), NULL );
00439        if ( rc == SQL_SUCCESS ) {
00440               if ( strcmp( DBMSName, "TimesTen" ) == 0 ||
00441                      strcmp( DBMSName, "Front-Tier" ) == 0 )
00442               {
00443                      Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
00444                             "TimesTen database!\n",
00445                             0, 0, 0 );
00446                      bi->sql_flags |= BSQLF_USE_REVERSE_DN;
00447               }
00448 
00449        } else {
00450               Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
00451                      "SQLGetInfo() failed.\n",
00452                      0, 0, 0 );
00453               backsql_PrintErrors( bi->sql_db_env, *dbhp, SQL_NULL_HENV, rc );
00454               SQLDisconnect( *dbhp );
00455               SQLFreeConnect( *dbhp );
00456               return LDAP_UNAVAILABLE;
00457        }
00458        /* end TimesTen */
00459 
00460        Debug( LDAP_DEBUG_TRACE, "<==backsql_open_db_handle()\n",
00461               0, 0, 0 );
00462 
00463        return LDAP_SUCCESS;
00464 }
00465 
00466 static void   *backsql_db_conn_dummy;
00467 
00468 static void
00469 backsql_db_conn_keyfree(
00470        void          *key,
00471        void          *data )
00472 {
00473        (void)backsql_close_db_handle( (SQLHDBC)data );
00474 }
00475 
00476 int
00477 backsql_free_db_conn( Operation *op, SQLHDBC dbh )
00478 {
00479        Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_conn()\n", 0, 0, 0 );
00480 
00481        (void)backsql_close_db_handle( dbh );
00482        ldap_pvt_thread_pool_setkey( op->o_threadctx,
00483               &backsql_db_conn_dummy, (void *)SQL_NULL_HDBC,
00484               backsql_db_conn_keyfree, NULL, NULL );
00485 
00486        Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_conn()\n", 0, 0, 0 );
00487 
00488        return LDAP_SUCCESS;
00489 }
00490 
00491 int
00492 backsql_get_db_conn( Operation *op, SQLHDBC *dbhp )
00493 {
00494        backsql_info  *bi = (backsql_info *)op->o_bd->be_private;
00495        int           rc = LDAP_SUCCESS;
00496        SQLHDBC              dbh = SQL_NULL_HDBC;
00497 
00498        Debug( LDAP_DEBUG_TRACE, "==>backsql_get_db_conn()\n", 0, 0, 0 );
00499 
00500        assert( dbhp != NULL );
00501        *dbhp = SQL_NULL_HDBC;
00502 
00503        if ( op->o_threadctx ) {
00504               void          *data = NULL;
00505 
00506               ldap_pvt_thread_pool_getkey( op->o_threadctx,
00507                             &backsql_db_conn_dummy, &data, NULL );
00508               dbh = (SQLHDBC)data;
00509 
00510        } else {
00511               dbh = bi->sql_dbh;
00512        }
00513 
00514        if ( dbh == SQL_NULL_HDBC ) {
00515               rc = backsql_open_db_handle( bi, &dbh );
00516               if ( rc != LDAP_SUCCESS ) {
00517                      return rc;
00518               }
00519 
00520               if ( op->o_threadctx ) {
00521                      void          *data = (void *)dbh;
00522 
00523                      ldap_pvt_thread_pool_setkey( op->o_threadctx,
00524                                    &backsql_db_conn_dummy, data,
00525                                    backsql_db_conn_keyfree, NULL, NULL );
00526 
00527               } else {
00528                      bi->sql_dbh = dbh;
00529               }
00530        }
00531 
00532        *dbhp = dbh;
00533 
00534        Debug( LDAP_DEBUG_TRACE, "<==backsql_get_db_conn()\n", 0, 0, 0 );
00535 
00536        return LDAP_SUCCESS;
00537 }
00538