]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/sql-wrap.c
c549c99b7b47c8034fb9128ce3d6f0b2edd4c1f6
[openldap] / servers / slapd / back-sql / sql-wrap.c
1 /*
2  *       Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
3  *
4  *       Redistribution and use in source and binary forms are permitted only
5  *       as authorized by the OpenLDAP Public License.  A copy of this
6  *       license is available at http://www.OpenLDAP.org/license.html or
7  *       in file LICENSE in the top-level directory of the distribution.
8  */
9
10 #include "portable.h"
11
12 #ifdef SLAPD_SQL
13
14 #include <stdio.h>
15 #include "ac/string.h"
16 #include <sys/types.h>
17 #include "ldap_pvt.h"
18 #include "slap.h"
19 #include "back-sql.h"
20 #include "sql-types.h"
21 #include "sql-wrap.h"
22 #include "schema-map.h"
23
24 #define MAX_ATTR_LEN 16384
25
26 typedef struct backsql_conn {
27         int             ldap_cid;
28         SQLHDBC         dbh;
29 } backsql_db_conn;
30
31 int backsql_dummy( void *, void * );
32
33 void
34 backsql_PrintErrors( SQLHENV henv, SQLHDBC hdbc, SQLHSTMT sth, int rc )
35 {
36         SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH];            /* msg. buffer    */
37         SQLCHAR state[SQL_SQLSTATE_SIZE];               /* statement buf. */
38         SDWORD  iSqlCode;                               /* return code    */
39         SWORD   len = SQL_MAX_MESSAGE_LENGTH - 1;       /* return length  */ 
40
41         Debug( LDAP_DEBUG_TRACE, "Return code: %d\n", rc, 0, 0 );
42
43
44         rc = SQLError( henv, hdbc, sth, state, &iSqlCode, msg,
45                         SQL_MAX_MESSAGE_LENGTH - 1, &len );
46         for ( ; BACKSQL_SUCCESS( rc ); ) {
47                 Debug( LDAP_DEBUG_TRACE, "Native error code: %d\n", 
48                                 (int)iSqlCode, 0, 0 );
49                 Debug( LDAP_DEBUG_TRACE, "SQL engine state: %s\n", 
50                                 state, 0, 0 );
51                 Debug( LDAP_DEBUG_TRACE, "Message: %s\n", msg, 0, 0 );
52                 rc = SQLError( henv, hdbc, sth, state, &iSqlCode, msg,
53                                 SQL_MAX_MESSAGE_LENGTH - 1, &len );
54         }
55 }
56
57 RETCODE
58 backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, char *query, int timeout )
59 {
60         RETCODE         rc;
61         char            drv_name[ 30 ];
62         SWORD           len;
63
64         rc = SQLAllocStmt( dbh, sth );
65         if ( rc != SQL_SUCCESS ) {
66                 return rc;
67         }
68
69 #if 0
70         Debug( LDAP_DEBUG_TRACE, "==>_SQLPrepare()\n", 0, 0, 0 );
71 #endif
72
73         SQLGetInfo( dbh, SQL_DRIVER_NAME, drv_name, 30, &len );
74
75 #if 0
76         Debug( LDAP_DEBUG_TRACE, "_SQLPrepare(): driver name='%s'\n",
77                         drv_name, 0, 0 );
78 #endif
79         
80         if ( !strncmp( ldap_pvt_str2upper( drv_name ), "SQLSRV32.DLL", 30 ) ) {
81                 /*
82                  * stupid default result set in MS SQL Server
83                  * does not support multiple active statements
84                  * on the same connection -- so we are trying 
85                  * to make it not to use default result set...
86                  */
87                 Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
88                         "enabling MS SQL Server default result "
89                         "set workaround\n", 0, 0, 0 );
90                 rc = SQLSetStmtOption( *sth, SQL_CONCURRENCY, 
91                                 SQL_CONCUR_ROWVER );
92                 if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) {
93                         Debug( LDAP_DEBUG_TRACE, "_SQLPrepare(): "
94                                 "SQLSetStmtOption(SQL_CONCURRENCY,SQL_CONCUR_ROWVER) failed:\n", 
95                                 0, 0, 0 );
96                         backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
97                 }
98         }
99
100         if ( timeout > 0 ) {
101                 Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
102                         "setting query timeout to %d sec.\n", 
103                         timeout, 0, 0 );
104                 rc = SQLSetStmtOption( *sth, SQL_QUERY_TIMEOUT, timeout );
105                 if ( rc != SQL_SUCCESS ) {
106                         backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
107                 }
108         }
109
110 #if 0
111         Debug( LDAP_DEBUG_TRACE, "<==_SQLPrepare() calling SQLPrepare()\n",
112                         0, 0, 0 );
113 #endif
114
115         return SQLPrepare( *sth, query, SQL_NTS );
116 }
117
118 #if 0
119 RETCODE
120 backsql_BindParamStr( SQLHSTMT sth, int par_ind, char *str, int maxlen )
121 {
122         RETCODE         rc;
123
124         rc = SQLBindParameter( sth, (SQLUSMALLINT)par_ind, SQL_PARAM_INPUT,
125                         SQL_C_CHAR, SQL_VARCHAR,
126                         (SQLUINTEGER)maxlen, 0, (SQLPOINTER)str,
127                         (SQLUINTEGER)maxlen, NULL );
128         return rc;
129 }
130
131 RETCODE
132 backsql_BindParamID( SQLHSTMT sth, int par_ind, unsigned long *id )
133 {
134         return SQLBindParameter( sth, (SQLUSMALLINT)par_ind,
135                         SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
136                         0, 0, (SQLPOINTER)id, 0, (SQLINTEGER*)NULL );
137 }
138 #endif
139
140 RETCODE
141 backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row )
142 {
143         RETCODE         rc;
144         SQLCHAR         colname[ 64 ];
145         SQLSMALLINT     name_len, col_type, col_scale, col_null;
146         UDWORD          col_prec;
147         int             i;
148
149         if ( row == NULL ) {
150                 return SQL_ERROR;
151         }
152
153 #if 0
154         Debug( LDAP_DEBUG_TRACE, "==> backsql_BindRowAsStrings()\n", 0, 0, 0 );
155 #endif
156         
157         rc = SQLNumResultCols( sth, &row->ncols );
158         if ( rc != SQL_SUCCESS ) {
159 #if 0
160                 Debug( LDAP_DEBUG_TRACE, "_SQLBindRowAsStrings(): "
161                         "SQLNumResultCols() failed:\n", 0, 0, 0 );
162 #endif
163                 
164                 backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC, sth, rc );
165         } else {
166 #if 0
167                 Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
168                         "ncols=%d\n", (int)row->ncols, 0, 0 );
169 #endif
170
171                 row->col_names = (char **)ch_calloc( row->ncols, 
172                                 sizeof( char * ) );
173                 row->cols = (char **)ch_calloc( row->ncols, sizeof( char * ) );
174                 row->col_prec = (UDWORD *)ch_calloc( row->ncols,
175                                 sizeof( UDWORD ) );
176                 row->is_null = (SQLINTEGER *)ch_calloc( row->ncols,
177                                 sizeof( SQLINTEGER ) );
178                 for ( i = 1; i <= row->ncols; i++ ) {
179                         rc = SQLDescribeCol( sth, (SQLSMALLINT)i, &colname[ 0 ],
180                                         (SQLUINTEGER)sizeof( colname ) - 1,
181                                         &name_len, &col_type,
182                                         &col_prec, &col_scale, &col_null );
183                         row->col_names[ i - 1 ] = ch_strdup( colname );
184 #if 0
185                         Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
186                                 "col_name=%s, col_prec[%d]=%d\n",
187                                 colname, (int)i, (int)col_prec );
188 #endif
189                         if ( col_type == SQL_LONGVARCHAR 
190                                         || col_type == SQL_LONGVARBINARY) {
191 #if 0
192                                 row->cols[ i - 1 ] = NULL;
193                                 row->col_prec[ i - 1 ] = -1;
194
195                                 /*
196                                  * such fields must be handled 
197                                  * in some other way since they return 2G 
198                                  * as their precision (at least it does so 
199                                  * with MS SQL Server w/native driver)
200                                  * for now, we just set fixed precision 
201                                  * for such fields - dirty hack, but...
202                                  * no time to deal with SQLGetData()
203                                  */
204 #endif
205                                 col_prec = MAX_ATTR_LEN;
206                                 row->cols[ i - 1 ] = (char *)ch_calloc( col_prec + 1, sizeof( char ) );
207                                 row->col_prec[ i - 1 ] = col_prec;
208                                 rc = SQLBindCol( sth, (SQLUSMALLINT)i,
209                                                 SQL_C_CHAR,
210                                                 (SQLPOINTER)row->cols[ i - 1 ],
211                                                 col_prec + 1,
212                                                 &row->is_null[ i - 1 ] );
213                         } else {
214                                 row->cols[ i - 1 ] = (char *)ch_calloc( col_prec + 1, sizeof( char ) );
215                                 row->col_prec[ i - 1 ] = col_prec;
216                                 rc = SQLBindCol( sth, (SQLUSMALLINT)i,
217                                                 SQL_C_CHAR,
218                                                 (SQLPOINTER)row->cols[ i - 1 ],
219                                                 col_prec + 1,
220                                                 &row->is_null[ i - 1 ] );
221                         }
222                 }
223         }
224 #if 0
225         Debug( LDAP_DEBUG_TRACE, "<== backsql_BindRowAsStrings()\n", 0, 0, 0 );
226 #endif
227
228         return rc;
229 }
230
231 RETCODE
232 backsql_FreeRow( BACKSQL_ROW_NTS *row )
233 {
234         int     i;
235
236         if ( row->cols == NULL ) {
237                 return SQL_ERROR;
238         }
239
240         for ( i = 0; i < row->ncols; i++ ) {
241                 /*
242                  * FIXME: we need to free the col_names as well, don't we?
243                  */
244                 free( row->cols[ i ] );
245         }
246
247         free( row->col_names );
248         free( row->col_prec );
249         free( row->cols );
250         free( row->is_null );
251
252         return SQL_SUCCESS;
253 }
254
255 int
256 backsql_cmp_connid( backsql_db_conn *c1, backsql_db_conn *c2 )
257 {
258         if ( c1->ldap_cid > c2->ldap_cid ) {
259                 return 1;
260         }
261         
262         if ( c1->ldap_cid < c2->ldap_cid ) {
263                 return -1;
264         }
265         
266         return 0;
267 }
268
269 int
270 backsql_close_db_conn( backsql_db_conn *conn )
271 {
272         Debug( LDAP_DEBUG_TRACE, "==>backsql_close_db_conn()\n", 0, 0, 0 );
273
274         /* TimesTen */
275         SQLTransact( SQL_NULL_HENV, conn->dbh, SQL_COMMIT );
276         SQLDisconnect( conn->dbh );
277         SQLFreeConnect( conn->dbh );
278         Debug( LDAP_DEBUG_TRACE, "<==backsql_close_db_conn()\n", 0, 0, 0 );
279         return 1;
280 }
281
282 int
283 backsql_init_db_env( backsql_info *si )
284 {
285         RETCODE         rc;
286         
287         Debug( LDAP_DEBUG_TRACE, "==>backsql_init_db_env()\n", 0, 0, 0 );
288         rc = SQLAllocEnv( &si->db_env );
289         if ( rc != SQL_SUCCESS ) {
290                 Debug( LDAP_DEBUG_TRACE, "init_db_env: SQLAllocEnv failed:\n",
291                                 0, 0, 0 );
292                 backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC,
293                                 SQL_NULL_HENV, rc );
294         }
295         Debug( LDAP_DEBUG_TRACE, "<==backsql_init_db_env()\n", 0, 0, 0 );
296         return SQL_SUCCESS;
297 }
298
299 int
300 backsql_free_db_env( backsql_info *si )
301 {
302         Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_env()\n", 0, 0, 0 );
303 #if 0
304         Debug( LDAP_DEBUG_TRACE, "free_db_env(): delete AVL tree here!!!\n",
305                         0, 0, 0 );
306 #endif
307
308         /*
309          * stop, if frontend waits for all threads to shutdown 
310          * before calling this -- then what are we going to delete?? 
311          * everything is already deleted...
312          */
313         Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_env()\n", 0, 0, 0 );
314         return SQL_SUCCESS;
315 }
316
317 backsql_db_conn *
318 backsql_open_db_conn( backsql_info *si, int ldap_cid )
319 {
320         /* TimesTen */
321         char                    DBMSName[ 32 ];
322         backsql_db_conn         *dbc;
323         int                     rc;
324  
325         Debug( LDAP_DEBUG_TRACE, "==>backsql_open_db_conn()\n", 0, 0, 0 );
326         dbc = (backsql_db_conn *)ch_calloc( 1, sizeof( backsql_db_conn ) );
327         dbc->ldap_cid = ldap_cid;
328         rc = SQLAllocConnect( si->db_env, &dbc->dbh );
329         if (!BACKSQL_SUCCESS( rc ) ) {
330                 Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
331                         "SQLAllocConnect() failed:\n", 0, 0, 0 );
332                 backsql_PrintErrors( si->db_env, SQL_NULL_HDBC,
333                                 SQL_NULL_HENV, rc );
334                 return NULL;
335         }
336
337         rc = SQLConnect( dbc->dbh, si->dbname, SQL_NTS, si->dbuser, 
338                         SQL_NTS, si->dbpasswd, SQL_NTS );
339         if ( rc != SQL_SUCCESS ) {
340                 Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
341                         "SQLConnect() to database '%s' as user '%s' "
342                         "%s:\n", si->dbname, si->dbuser,
343                         rc == SQL_SUCCESS_WITH_INFO ?
344                         "succeeded with info" : "failed" );
345                 backsql_PrintErrors( si->db_env, dbc->dbh, SQL_NULL_HENV, rc );
346                 if ( rc != SQL_SUCCESS_WITH_INFO ) {
347                         return NULL;
348                 }
349         }
350
351         /* 
352          * TimesTen : Turn off autocommit.  We must explicitly
353          * commit any transactions. 
354          */
355         SQLSetConnectOption( dbc->dbh, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF );
356
357         /* 
358          * See if this connection is to TimesTen.  If it is,
359          * remember that fact for later use.
360          */
361         si->isTimesTen = 0;     /* Assume until proven otherwise */
362         DBMSName[ 0 ] = '\0';
363         rc = SQLGetInfo( dbc->dbh, SQL_DBMS_NAME, (PTR)&DBMSName,
364                         sizeof( DBMSName ), NULL );
365         if ( rc == SQL_SUCCESS ) {
366                 if ( strcmp( DBMSName, "TimesTen" ) == 0 ||
367                                 strcmp( DBMSName, "Front-Tier" ) == 0 ) {
368                         Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
369                                 "TimesTen database!\n", 0, 0, 0 );
370                         si->isTimesTen = 1;
371                 }
372         } else {
373                 Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
374                         "SQLGetInfo() failed:\n", 0, 0, 0 );
375                 backsql_PrintErrors( si->db_env, dbc->dbh, SQL_NULL_HENV, rc );
376         }
377         /* end TimesTen */
378
379         Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn(): "
380                 "connected, adding to tree\n", 0, 0, 0 );
381         ldap_pvt_thread_mutex_lock( &si->dbconn_mutex );
382         avl_insert( &si->db_conns, dbc, (AVL_CMP)backsql_cmp_connid,
383                         backsql_dummy );
384         ldap_pvt_thread_mutex_unlock( &si->dbconn_mutex );
385         Debug( LDAP_DEBUG_TRACE, "<==backsql_open_db_conn()\n", 0, 0, 0 );
386
387         return dbc;
388 }
389
390 int
391 backsql_free_db_conn( Backend *be, Connection *ldapc )
392 {
393         backsql_info            *si = (backsql_info *)be->be_private;
394         backsql_db_conn         tmp, *conn;
395
396         Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_conn()\n", 0, 0, 0 );
397         tmp.ldap_cid = ldapc->c_connid;
398         ldap_pvt_thread_mutex_lock( &si->dbconn_mutex );
399         conn = (backsql_db_conn *)avl_delete( &si->db_conns, &tmp,
400                         (AVL_CMP)backsql_cmp_connid );
401         ldap_pvt_thread_mutex_unlock( &si->dbconn_mutex );
402
403         /*
404          * we have one thread per connection, as I understand -- so we can
405          * get this out of critical section
406          */
407         if ( conn != NULL ) {
408                 Debug( LDAP_DEBUG_TRACE, "backsql_free_db_conn(): "
409                         "closing db connection\n", 0, 0, 0 );
410                 backsql_close_db_conn( conn );
411         }
412         Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_conn()\n", 0, 0, 0 );
413         return SQL_SUCCESS;
414 }
415
416 SQLHDBC
417 backsql_get_db_conn( Backend *be, Connection *ldapc )
418 {
419         backsql_info            *si = (backsql_info *)be->be_private;
420         backsql_db_conn         *dbc;
421         backsql_db_conn         tmp;
422
423         Debug( LDAP_DEBUG_TRACE, "==>backsql_get_db_conn()\n", 0, 0, 0 );
424
425         tmp.ldap_cid = ldapc->c_connid;
426
427         /*
428          * we have one thread per connection, as I understand -- 
429          * so we do not need locking here
430          */
431         dbc = (backsql_db_conn *)avl_find( si->db_conns, &tmp,
432                         (AVL_CMP)backsql_cmp_connid );
433         if ( !dbc ) {
434                 dbc = backsql_open_db_conn( si, ldapc->c_connid );
435         }
436  
437         if ( !dbc ) {
438                 Debug( LDAP_DEBUG_TRACE, "backsql_get_db_conn(): "
439                         "could not get connection handle -- returning NULL\n",
440                         0, 0, 0 );
441                 return SQL_NULL_HDBC;
442         }
443         ldap_pvt_thread_mutex_lock( &si->schema_mutex );
444         if ( !si->schema_loaded ) {
445                 Debug( LDAP_DEBUG_TRACE, "backsql_get_db_conn(): "
446                         "first call -- reading schema map\n", 0, 0, 0 );
447                 backsql_load_schema_map( si, dbc->dbh );
448         }
449         ldap_pvt_thread_mutex_unlock( &si->schema_mutex );
450
451         Debug( LDAP_DEBUG_TRACE, "<==backsql_get_db_conn()\n", 0, 0, 0 );
452         return dbc->dbh;
453 }
454
455 #endif /* SLAPD_SQL */