]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/sql-wrap.c
Happy New Year
[openldap] / servers / slapd / back-sql / sql-wrap.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2018 The OpenLDAP Foundation.
5  * Portions Copyright 1999 Dmitry Kovalev.
6  * Portions Copyright 2002 Pierangelo Masarati.
7  * Portions Copyright 2004 Mark Adamson.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Dmitry Kovalev for inclusion
20  * by OpenLDAP Software.  Additional significant contributors include
21  * Pierangelo Masarati and Mark Adamson.
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27 #include "ac/string.h"
28 #include <sys/types.h>
29
30 #include "slap.h"
31 #include "proto-sql.h"
32
33 #define MAX_ATTR_LEN 16384
34
35 void
36 backsql_PrintErrors( SQLHENV henv, SQLHDBC hdbc, SQLHSTMT sth, int rc )
37 {
38         SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH];            /* msg. buffer    */
39         SQLCHAR state[SQL_SQLSTATE_SIZE];               /* statement buf. */
40         SDWORD  iSqlCode;                               /* return code    */
41         SWORD   len = SQL_MAX_MESSAGE_LENGTH - 1;       /* return length  */ 
42
43         Debug( LDAP_DEBUG_TRACE, "Return code: %d\n", rc, 0, 0 );
44
45         for ( ; rc = SQLError( henv, hdbc, sth, state, &iSqlCode, msg,
46                 SQL_MAX_MESSAGE_LENGTH - 1, &len ), BACKSQL_SUCCESS( rc ); )
47         {
48                 Debug( LDAP_DEBUG_TRACE,
49                         "   nativeErrCode=%d SQLengineState=%s msg=\"%s\"\n",
50                         (int)iSqlCode, state, msg );
51         }
52 }
53
54 RETCODE
55 backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, const char *query, int timeout )
56 {
57         RETCODE         rc;
58
59         rc = SQLAllocStmt( dbh, sth );
60         if ( rc != SQL_SUCCESS ) {
61                 return rc;
62         }
63
64 #ifdef BACKSQL_TRACE
65         Debug( LDAP_DEBUG_TRACE, "==>backsql_Prepare()\n", 0, 0, 0 );
66 #endif /* BACKSQL_TRACE */
67
68 #ifdef BACKSQL_MSSQL_WORKAROUND
69         {
70                 char            drv_name[ 30 ];
71                 SWORD           len;
72
73                 SQLGetInfo( dbh, SQL_DRIVER_NAME, drv_name, sizeof( drv_name ), &len );
74
75 #ifdef BACKSQL_TRACE
76                 Debug( LDAP_DEBUG_TRACE, "backsql_Prepare(): driver name=\"%s\"\n",
77                                 drv_name, 0, 0 );
78 #endif /* BACKSQL_TRACE */
79
80                 ldap_pvt_str2upper( drv_name );
81                 if ( !strncmp( drv_name, "SQLSRV32.DLL", STRLENOF( "SQLSRV32.DLL" ) ) ) {
82                         /*
83                          * stupid default result set in MS SQL Server
84                          * does not support multiple active statements
85                          * on the same connection -- so we are trying 
86                          * to make it not to use default result set...
87                          */
88                         Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
89                                 "enabling MS SQL Server default result "
90                                 "set workaround\n", 0, 0, 0 );
91                         rc = SQLSetStmtOption( *sth, SQL_CONCURRENCY, 
92                                         SQL_CONCUR_ROWVER );
93                         if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) {
94                                 Debug( LDAP_DEBUG_TRACE, "backsql_Prepare(): "
95                                         "SQLSetStmtOption(SQL_CONCURRENCY,"
96                                         "SQL_CONCUR_ROWVER) failed:\n", 
97                                         0, 0, 0 );
98                                 backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
99                                 SQLFreeStmt( *sth, SQL_DROP );
100                                 return rc;
101                         }
102                 }
103         }
104 #endif /* BACKSQL_MSSQL_WORKAROUND */
105
106         if ( timeout > 0 ) {
107                 Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
108                         "setting query timeout to %d sec.\n", 
109                         timeout, 0, 0 );
110                 rc = SQLSetStmtOption( *sth, SQL_QUERY_TIMEOUT, timeout );
111                 if ( rc != SQL_SUCCESS ) {
112                         backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
113                         SQLFreeStmt( *sth, SQL_DROP );
114                         return rc;
115                 }
116         }
117
118 #ifdef BACKSQL_TRACE
119         Debug( LDAP_DEBUG_TRACE, "<==backsql_Prepare() calling SQLPrepare()\n",
120                         0, 0, 0 );
121 #endif /* BACKSQL_TRACE */
122
123         return SQLPrepare( *sth, (SQLCHAR *)query, SQL_NTS );
124 }
125
126 RETCODE
127 backsql_BindRowAsStrings_x( SQLHSTMT sth, BACKSQL_ROW_NTS *row, void *ctx )
128 {
129         RETCODE         rc;
130
131         if ( row == NULL ) {
132                 return SQL_ERROR;
133         }
134
135 #ifdef BACKSQL_TRACE
136         Debug( LDAP_DEBUG_TRACE, "==> backsql_BindRowAsStrings()\n", 0, 0, 0 );
137 #endif /* BACKSQL_TRACE */
138         
139         rc = SQLNumResultCols( sth, &row->ncols );
140         if ( rc != SQL_SUCCESS ) {
141 #ifdef BACKSQL_TRACE
142                 Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings(): "
143                         "SQLNumResultCols() failed:\n", 0, 0, 0 );
144 #endif /* BACKSQL_TRACE */
145                 
146                 backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC, sth, rc );
147
148         } else {
149                 SQLCHAR         colname[ 64 ];
150                 SQLSMALLINT     name_len, col_type, col_scale, col_null;
151                 SQLLEN          col_prec;
152                 int             i;
153
154 #ifdef BACKSQL_TRACE
155                 Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
156                         "ncols=%d\n", (int)row->ncols, 0, 0 );
157 #endif /* BACKSQL_TRACE */
158
159                 row->col_names = (BerVarray)ber_memcalloc_x( row->ncols + 1, 
160                                 sizeof( struct berval ), ctx );
161                 if ( row->col_names == NULL ) {
162                         goto nomem;
163                 }
164
165                 row->col_prec = (UDWORD *)ber_memcalloc_x( row->ncols,
166                                 sizeof( UDWORD ), ctx );
167                 if ( row->col_prec == NULL ) {
168                         goto nomem;
169                 }
170
171                 row->col_type = (SQLSMALLINT *)ber_memcalloc_x( row->ncols,
172                                 sizeof( SQLSMALLINT ), ctx );
173                 if ( row->col_type == NULL ) {
174                         goto nomem;
175                 }
176
177                 row->cols = (char **)ber_memcalloc_x( row->ncols + 1, 
178                                 sizeof( char * ), ctx );
179                 if ( row->cols == NULL ) {
180                         goto nomem;
181                 }
182
183                 row->value_len = (SQLLEN *)ber_memcalloc_x( row->ncols,
184                                 sizeof( SQLLEN ), ctx );
185                 if ( row->value_len == NULL ) {
186                         goto nomem;
187                 }
188
189                 if ( 0 ) {
190 nomem:
191                         ber_memfree_x( row->col_names, ctx );
192                         row->col_names = NULL;
193                         ber_memfree_x( row->col_prec, ctx );
194                         row->col_prec = NULL;
195                         ber_memfree_x( row->col_type, ctx );
196                         row->col_type = NULL;
197                         ber_memfree_x( row->cols, ctx );
198                         row->cols = NULL;
199                         ber_memfree_x( row->value_len, ctx );
200                         row->value_len = NULL;
201
202                         Debug( LDAP_DEBUG_ANY, "backsql_BindRowAsStrings: "
203                                 "out of memory\n", 0, 0, 0 );
204
205                         return LDAP_NO_MEMORY;
206                 }
207
208                 for ( i = 0; i < row->ncols; i++ ) {
209                         SQLSMALLINT     TargetType;
210
211                         rc = SQLDescribeCol( sth, (SQLSMALLINT)(i + 1), &colname[ 0 ],
212                                         (SQLUINTEGER)( sizeof( colname ) - 1 ),
213                                         &name_len, &col_type,
214                                         &col_prec, &col_scale, &col_null );
215                         /* FIXME: test rc? */
216
217                         ber_str2bv_x( (char *)colname, 0, 1,
218                                         &row->col_names[ i ], ctx );
219 #ifdef BACKSQL_TRACE
220                         Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
221                                 "col_name=%s, col_prec[%d]=%d\n",
222                                 colname, (int)(i + 1), (int)col_prec );
223 #endif /* BACKSQL_TRACE */
224                         if ( col_type != SQL_CHAR && col_type != SQL_VARCHAR )
225                         {
226                                 col_prec = MAX_ATTR_LEN;
227                         }
228
229                         row->cols[ i ] = (char *)ber_memcalloc_x( col_prec + 1,
230                                         sizeof( char ), ctx );
231                         row->col_prec[ i ] = col_prec;
232                         row->col_type[ i ] = col_type;
233
234                         /*
235                          * ITS#3386, ITS#3113 - 20070308
236                          * Note: there are many differences between various DPMS and ODBC
237                          * Systems; some support SQL_C_BLOB, SQL_C_BLOB_LOCATOR.  YMMV:
238                          * This has only been tested on Linux/MySQL/UnixODBC
239                          * For BINARY-type Fields (BLOB, etc), read the data as BINARY
240                          */
241                         if ( BACKSQL_IS_BINARY( col_type ) ) {
242 #ifdef BACKSQL_TRACE
243                                 Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
244                                         "col_name=%s, col_type[%d]=%d: reading binary data\n",
245                                         colname, (int)(i + 1), (int)col_type);
246 #endif /* BACKSQL_TRACE */
247                                 TargetType = SQL_C_BINARY;
248
249                         } else {
250                                 /* Otherwise read it as Character data */
251 #ifdef BACKSQL_TRACE
252                                 Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
253                                         "col_name=%s, col_type[%d]=%d: reading character data\n",
254                                         colname, (int)(i + 1), (int)col_type);
255 #endif /* BACKSQL_TRACE */
256                                 TargetType = SQL_C_CHAR;
257                         }
258
259                         rc = SQLBindCol( sth, (SQLUSMALLINT)(i + 1),
260                                  TargetType,
261                                  (SQLPOINTER)row->cols[ i ],
262                                  col_prec + 1,
263                                  &row->value_len[ i ] );
264
265                         /* FIXME: test rc? */
266                 }
267
268                 BER_BVZERO( &row->col_names[ i ] );
269                 row->cols[ i ] = NULL;
270         }
271
272 #ifdef BACKSQL_TRACE
273         Debug( LDAP_DEBUG_TRACE, "<== backsql_BindRowAsStrings()\n", 0, 0, 0 );
274 #endif /* BACKSQL_TRACE */
275
276         return rc;
277 }
278
279 RETCODE
280 backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row )
281 {
282         return backsql_BindRowAsStrings_x( sth, row, NULL );
283 }
284
285 RETCODE
286 backsql_FreeRow_x( BACKSQL_ROW_NTS *row, void *ctx )
287 {
288         if ( row->cols == NULL ) {
289                 return SQL_ERROR;
290         }
291
292         ber_bvarray_free_x( row->col_names, ctx );
293         ber_memfree_x( row->col_prec, ctx );
294         ber_memfree_x( row->col_type, ctx );
295         ber_memvfree_x( (void **)row->cols, ctx );
296         ber_memfree_x( row->value_len, ctx );
297
298         return SQL_SUCCESS;
299 }
300
301
302 RETCODE
303 backsql_FreeRow( BACKSQL_ROW_NTS *row )
304 {
305         return backsql_FreeRow_x( row, NULL );
306 }
307
308 static void
309 backsql_close_db_handle( SQLHDBC dbh )
310 {
311         if ( dbh == SQL_NULL_HDBC ) {
312                 return;
313         }
314
315         Debug( LDAP_DEBUG_TRACE, "==>backsql_close_db_handle(%p)\n",
316                 (void *)dbh, 0, 0 );
317
318         /*
319          * Default transact is SQL_ROLLBACK; commit is required only
320          * by write operations, and it is explicitly performed after
321          * each atomic operation succeeds.
322          */
323
324         /* TimesTen */
325         SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
326         SQLDisconnect( dbh );
327         SQLFreeConnect( dbh );
328
329         Debug( LDAP_DEBUG_TRACE, "<==backsql_close_db_handle(%p)\n",
330                 (void *)dbh, 0, 0 );
331 }
332
333 int
334 backsql_conn_destroy(
335         backsql_info    *bi )
336 {
337         return 0;
338 }
339
340 int
341 backsql_init_db_env( backsql_info *bi )
342 {
343         RETCODE         rc;
344         int             ret = SQL_SUCCESS;
345         
346         Debug( LDAP_DEBUG_TRACE, "==>backsql_init_db_env()\n", 0, 0, 0 );
347
348         rc = SQLAllocEnv( &bi->sql_db_env );
349         if ( rc != SQL_SUCCESS ) {
350                 Debug( LDAP_DEBUG_TRACE, "init_db_env: SQLAllocEnv failed:\n",
351                                 0, 0, 0 );
352                 backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC,
353                                 SQL_NULL_HENV, rc );
354                 ret = SQL_ERROR;
355         }
356
357         Debug( LDAP_DEBUG_TRACE, "<==backsql_init_db_env()=%d\n", ret, 0, 0 );
358
359         return ret;
360 }
361
362 int
363 backsql_free_db_env( backsql_info *bi )
364 {
365         Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_env()\n", 0, 0, 0 );
366
367         (void)SQLFreeEnv( bi->sql_db_env );
368         bi->sql_db_env = SQL_NULL_HENV;
369
370         /*
371          * stop, if frontend waits for all threads to shutdown 
372          * before calling this -- then what are we going to delete?? 
373          * everything is already deleted...
374          */
375         Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_env()\n", 0, 0, 0 );
376
377         return SQL_SUCCESS;
378 }
379
380 static int
381 backsql_open_db_handle(
382         backsql_info    *bi,
383         SQLHDBC         *dbhp )
384 {
385         /* TimesTen */
386         char                    DBMSName[ 32 ];
387         int                     rc;
388
389         assert( dbhp != NULL );
390         *dbhp = SQL_NULL_HDBC;
391  
392         Debug( LDAP_DEBUG_TRACE, "==>backsql_open_db_handle()\n",
393                 0, 0, 0 );
394
395         rc = SQLAllocConnect( bi->sql_db_env, dbhp );
396         if ( !BACKSQL_SUCCESS( rc ) ) {
397                 Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
398                         "SQLAllocConnect() failed:\n",
399                         0, 0, 0 );
400                 backsql_PrintErrors( bi->sql_db_env, SQL_NULL_HDBC,
401                         SQL_NULL_HENV, rc );
402                 return LDAP_UNAVAILABLE;
403         }
404
405         rc = SQLConnect( *dbhp,
406                 (SQLCHAR*)bi->sql_dbname, SQL_NTS,
407                 (SQLCHAR*)bi->sql_dbuser, SQL_NTS,
408                 (SQLCHAR*)bi->sql_dbpasswd, SQL_NTS );
409         if ( rc != SQL_SUCCESS ) {
410                 Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
411                         "SQLConnect() to database \"%s\" %s.\n",
412                         bi->sql_dbname,
413                         rc == SQL_SUCCESS_WITH_INFO ?
414                                 "succeeded with info" : "failed",
415                         0 );
416                 backsql_PrintErrors( bi->sql_db_env, *dbhp, SQL_NULL_HENV, rc );
417                 if ( rc != SQL_SUCCESS_WITH_INFO ) {
418                         SQLFreeConnect( *dbhp );
419                         return LDAP_UNAVAILABLE;
420                 }
421         }
422
423         /* 
424          * TimesTen : Turn off autocommit.  We must explicitly
425          * commit any transactions. 
426          */
427         SQLSetConnectOption( *dbhp, SQL_AUTOCOMMIT,
428                 BACKSQL_AUTOCOMMIT_ON( bi ) ?  SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF );
429
430         /* 
431          * See if this connection is to TimesTen.  If it is,
432          * remember that fact for later use.
433          */
434         /* Assume until proven otherwise */
435         bi->sql_flags &= ~BSQLF_USE_REVERSE_DN;
436         DBMSName[ 0 ] = '\0';
437         rc = SQLGetInfo( *dbhp, SQL_DBMS_NAME, (PTR)&DBMSName,
438                         sizeof( DBMSName ), NULL );
439         if ( rc == SQL_SUCCESS ) {
440                 if ( strcmp( DBMSName, "TimesTen" ) == 0 ||
441                         strcmp( DBMSName, "Front-Tier" ) == 0 )
442                 {
443                         Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
444                                 "TimesTen database!\n",
445                                 0, 0, 0 );
446                         bi->sql_flags |= BSQLF_USE_REVERSE_DN;
447                 }
448
449         } else {
450                 Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
451                         "SQLGetInfo() failed.\n",
452                         0, 0, 0 );
453                 backsql_PrintErrors( bi->sql_db_env, *dbhp, SQL_NULL_HENV, rc );
454                 SQLDisconnect( *dbhp );
455                 SQLFreeConnect( *dbhp );
456                 return LDAP_UNAVAILABLE;
457         }
458         /* end TimesTen */
459
460         Debug( LDAP_DEBUG_TRACE, "<==backsql_open_db_handle()\n",
461                 0, 0, 0 );
462
463         return LDAP_SUCCESS;
464 }
465
466 static void     *backsql_db_conn_dummy;
467
468 static void
469 backsql_db_conn_keyfree(
470         void            *key,
471         void            *data )
472 {
473         (void)backsql_close_db_handle( (SQLHDBC)data );
474 }
475
476 int
477 backsql_free_db_conn( Operation *op, SQLHDBC dbh )
478 {
479         Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_conn()\n", 0, 0, 0 );
480
481         (void)backsql_close_db_handle( dbh );
482         ldap_pvt_thread_pool_setkey( op->o_threadctx,
483                 &backsql_db_conn_dummy, (void *)SQL_NULL_HDBC,
484                 backsql_db_conn_keyfree, NULL, NULL );
485
486         Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_conn()\n", 0, 0, 0 );
487
488         return LDAP_SUCCESS;
489 }
490
491 int
492 backsql_get_db_conn( Operation *op, SQLHDBC *dbhp )
493 {
494         backsql_info    *bi = (backsql_info *)op->o_bd->be_private;
495         int             rc = LDAP_SUCCESS;
496         SQLHDBC         dbh = SQL_NULL_HDBC;
497
498         Debug( LDAP_DEBUG_TRACE, "==>backsql_get_db_conn()\n", 0, 0, 0 );
499
500         assert( dbhp != NULL );
501         *dbhp = SQL_NULL_HDBC;
502
503         if ( op->o_threadctx ) {
504                 void            *data = NULL;
505
506                 ldap_pvt_thread_pool_getkey( op->o_threadctx,
507                                 &backsql_db_conn_dummy, &data, NULL );
508                 dbh = (SQLHDBC)data;
509
510         } else {
511                 dbh = bi->sql_dbh;
512         }
513
514         if ( dbh == SQL_NULL_HDBC ) {
515                 rc = backsql_open_db_handle( bi, &dbh );
516                 if ( rc != LDAP_SUCCESS ) {
517                         return rc;
518                 }
519
520                 if ( op->o_threadctx ) {
521                         void            *data = (void *)dbh;
522
523                         ldap_pvt_thread_pool_setkey( op->o_threadctx,
524                                         &backsql_db_conn_dummy, data,
525                                         backsql_db_conn_keyfree, NULL, NULL );
526
527                 } else {
528                         bi->sql_dbh = dbh;
529                 }
530         }
531
532         *dbhp = dbh;
533
534         Debug( LDAP_DEBUG_TRACE, "<==backsql_get_db_conn()\n", 0, 0, 0 );
535
536         return LDAP_SUCCESS;
537 }
538