1 /* $OpenLDAP: pkg/ldap/servers/slapd/back-sql/sql-wrap.c,v 1.43.2.5 2008/02/11 23:26:48 kurt Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2008 The OpenLDAP Foundation.
5 * Portions Copyright 1999 Dmitry Kovalev.
6 * Portions Copyright 2002 Pierangelo Masarati.
7 * Portions Copyright 2004 Mark Adamson.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
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>.
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.
27 #include "ac/string.h"
28 #include <sys/types.h>
31 #include "proto-sql.h"
33 #define MAX_ATTR_LEN 16384
36 backsql_PrintErrors( SQLHENV henv
, SQLHDBC hdbc
, SQLHSTMT sth
, int rc
)
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 */
43 Debug( LDAP_DEBUG_TRACE
, "Return code: %d\n", rc
, 0, 0 );
45 for ( ; rc
= SQLError( henv
, hdbc
, sth
, state
, &iSqlCode
, msg
,
46 SQL_MAX_MESSAGE_LENGTH
- 1, &len
), BACKSQL_SUCCESS( rc
); )
48 Debug( LDAP_DEBUG_TRACE
,
49 " nativeErrCode=%d SQLengineState=%s msg=\"%s\"\n",
50 (int)iSqlCode
, state
, msg
);
55 backsql_Prepare( SQLHDBC dbh
, SQLHSTMT
*sth
, char *query
, int timeout
)
59 rc
= SQLAllocStmt( dbh
, sth
);
60 if ( rc
!= SQL_SUCCESS
) {
65 Debug( LDAP_DEBUG_TRACE
, "==>backsql_Prepare()\n", 0, 0, 0 );
66 #endif /* BACKSQL_TRACE */
68 #ifdef BACKSQL_MSSQL_WORKAROUND
73 SQLGetInfo( dbh
, SQL_DRIVER_NAME
, drv_name
, sizeof( drv_name
), &len
);
76 Debug( LDAP_DEBUG_TRACE
, "backsql_Prepare(): driver name=\"%s\"\n",
78 #endif /* BACKSQL_TRACE */
80 ldap_pvt_str2upper( drv_name
);
81 if ( !strncmp( drv_name
, "SQLSRV32.DLL", STRLENOF( "SQLSRV32.DLL" ) ) ) {
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...
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
,
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",
98 backsql_PrintErrors( SQL_NULL_HENV
, dbh
, *sth
, rc
);
99 SQLFreeStmt( *sth
, SQL_DROP
);
104 #endif /* BACKSQL_MSSQL_WORKAROUND */
107 Debug( LDAP_DEBUG_TRACE
, "_SQLprepare(): "
108 "setting query timeout to %d sec.\n",
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
);
119 Debug( LDAP_DEBUG_TRACE
, "<==backsql_Prepare() calling SQLPrepare()\n",
121 #endif /* BACKSQL_TRACE */
123 return SQLPrepare( *sth
, (SQLCHAR
*)query
, SQL_NTS
);
127 backsql_BindRowAsStrings_x( SQLHSTMT sth
, BACKSQL_ROW_NTS
*row
, void *ctx
)
136 Debug( LDAP_DEBUG_TRACE
, "==> backsql_BindRowAsStrings()\n", 0, 0, 0 );
137 #endif /* BACKSQL_TRACE */
139 rc
= SQLNumResultCols( sth
, &row
->ncols
);
140 if ( rc
!= SQL_SUCCESS
) {
142 Debug( LDAP_DEBUG_TRACE
, "backsql_BindRowAsStrings(): "
143 "SQLNumResultCols() failed:\n", 0, 0, 0 );
144 #endif /* BACKSQL_TRACE */
146 backsql_PrintErrors( SQL_NULL_HENV
, SQL_NULL_HDBC
, sth
, rc
);
149 SQLCHAR colname
[ 64 ];
150 SQLSMALLINT name_len
, col_type
, col_scale
, col_null
;
155 Debug( LDAP_DEBUG_TRACE
, "backsql_BindRowAsStrings: "
156 "ncols=%d\n", (int)row
->ncols
, 0, 0 );
157 #endif /* BACKSQL_TRACE */
159 row
->col_names
= (BerVarray
)ber_memcalloc_x( row
->ncols
+ 1,
160 sizeof( struct berval
), ctx
);
161 if ( row
->col_names
== NULL
) {
165 row
->col_prec
= (UDWORD
*)ber_memcalloc_x( row
->ncols
,
166 sizeof( UDWORD
), ctx
);
167 if ( row
->col_prec
== NULL
) {
171 row
->col_type
= (SQLSMALLINT
*)ber_memcalloc_x( row
->ncols
,
172 sizeof( SQLSMALLINT
), ctx
);
173 if ( row
->col_type
== NULL
) {
177 row
->cols
= (char **)ber_memcalloc_x( row
->ncols
+ 1,
178 sizeof( char * ), ctx
);
179 if ( row
->cols
== NULL
) {
183 row
->value_len
= (SQLINTEGER
*)ber_memcalloc_x( row
->ncols
,
184 sizeof( SQLINTEGER
), ctx
);
185 if ( row
->value_len
== NULL
) {
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
);
199 ber_memfree_x( row
->value_len
, ctx
);
200 row
->value_len
= NULL
;
202 Debug( LDAP_DEBUG_ANY
, "backsql_BindRowAsStrings: "
203 "out of memory\n", 0, 0, 0 );
205 return LDAP_NO_MEMORY
;
208 for ( i
= 0; i
< row
->ncols
; i
++ ) {
209 SQLSMALLINT TargetType
;
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? */
217 ber_str2bv_x( (char *)colname
, 0, 1,
218 &row
->col_names
[ i
], ctx
);
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
)
226 col_prec
= MAX_ATTR_LEN
;
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
;
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
241 if ( BACKSQL_IS_BINARY( col_type
) ) {
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
;
250 /* Otherwise read it as Character data */
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
;
259 rc
= SQLBindCol( sth
, (SQLUSMALLINT
)(i
+ 1),
261 (SQLPOINTER
)row
->cols
[ i
],
263 &row
->value_len
[ i
] );
265 /* FIXME: test rc? */
268 BER_BVZERO( &row
->col_names
[ i
] );
269 row
->cols
[ i
] = NULL
;
273 Debug( LDAP_DEBUG_TRACE
, "<== backsql_BindRowAsStrings()\n", 0, 0, 0 );
274 #endif /* BACKSQL_TRACE */
280 backsql_BindRowAsStrings( SQLHSTMT sth
, BACKSQL_ROW_NTS
*row
)
282 return backsql_BindRowAsStrings_x( sth
, row
, NULL
);
286 backsql_FreeRow_x( BACKSQL_ROW_NTS
*row
, void *ctx
)
288 if ( row
->cols
== NULL
) {
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
);
303 backsql_FreeRow( BACKSQL_ROW_NTS
*row
)
305 return backsql_FreeRow_x( row
, NULL
);
309 backsql_close_db_handle( SQLHDBC dbh
)
311 if ( dbh
== SQL_NULL_HDBC
) {
315 Debug( LDAP_DEBUG_TRACE
, "==>backsql_close_db_handle(%p)\n",
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.
325 SQLTransact( SQL_NULL_HENV
, dbh
, SQL_ROLLBACK
);
326 SQLDisconnect( dbh
);
327 SQLFreeConnect( dbh
);
329 Debug( LDAP_DEBUG_TRACE
, "<==backsql_close_db_handle(%p)\n",
334 backsql_conn_destroy(
341 backsql_init_db_env( backsql_info
*bi
)
344 int ret
= SQL_SUCCESS
;
346 Debug( LDAP_DEBUG_TRACE
, "==>backsql_init_db_env()\n", 0, 0, 0 );
348 rc
= SQLAllocEnv( &bi
->sql_db_env
);
349 if ( rc
!= SQL_SUCCESS
) {
350 Debug( LDAP_DEBUG_TRACE
, "init_db_env: SQLAllocEnv failed:\n",
352 backsql_PrintErrors( SQL_NULL_HENV
, SQL_NULL_HDBC
,
357 Debug( LDAP_DEBUG_TRACE
, "<==backsql_init_db_env()=%d\n", ret
, 0, 0 );
363 backsql_free_db_env( backsql_info
*bi
)
365 Debug( LDAP_DEBUG_TRACE
, "==>backsql_free_db_env()\n", 0, 0, 0 );
367 (void)SQLFreeEnv( bi
->sql_db_env
);
368 bi
->sql_db_env
= SQL_NULL_HENV
;
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...
375 Debug( LDAP_DEBUG_TRACE
, "<==backsql_free_db_env()\n", 0, 0, 0 );
381 backsql_open_db_handle(
389 assert( dbhp
!= NULL
);
390 *dbhp
= SQL_NULL_HDBC
;
392 Debug( LDAP_DEBUG_TRACE
, "==>backsql_open_db_handle()\n",
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",
400 backsql_PrintErrors( bi
->sql_db_env
, SQL_NULL_HDBC
,
402 return LDAP_UNAVAILABLE
;
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",
413 rc
== SQL_SUCCESS_WITH_INFO
?
414 "succeeded with info" : "failed",
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
;
424 * TimesTen : Turn off autocommit. We must explicitly
425 * commit any transactions.
427 SQLSetConnectOption( *dbhp
, SQL_AUTOCOMMIT
, SQL_AUTOCOMMIT_OFF
);
430 * See if this connection is to TimesTen. If it is,
431 * remember that fact for later use.
433 /* Assume until proven otherwise */
434 bi
->sql_flags
&= ~BSQLF_USE_REVERSE_DN
;
435 DBMSName
[ 0 ] = '\0';
436 rc
= SQLGetInfo( *dbhp
, SQL_DBMS_NAME
, (PTR
)&DBMSName
,
437 sizeof( DBMSName
), NULL
);
438 if ( rc
== SQL_SUCCESS
) {
439 if ( strcmp( DBMSName
, "TimesTen" ) == 0 ||
440 strcmp( DBMSName
, "Front-Tier" ) == 0 )
442 Debug( LDAP_DEBUG_TRACE
, "backsql_open_db_handle(): "
443 "TimesTen database!\n",
445 bi
->sql_flags
|= BSQLF_USE_REVERSE_DN
;
449 Debug( LDAP_DEBUG_TRACE
, "backsql_open_db_handle(): "
450 "SQLGetInfo() failed.\n",
452 backsql_PrintErrors( bi
->sql_db_env
, *dbhp
, SQL_NULL_HENV
, rc
);
453 SQLDisconnect( *dbhp
);
454 SQLFreeConnect( *dbhp
);
455 return LDAP_UNAVAILABLE
;
459 Debug( LDAP_DEBUG_TRACE
, "<==backsql_open_db_handle()\n",
466 backsql_free_db_conn( Operation
*op
, SQLHDBC dbh
)
468 Debug( LDAP_DEBUG_TRACE
, "==>backsql_free_db_conn()\n", 0, 0, 0 );
470 (void)backsql_close_db_handle( dbh
);
472 Debug( LDAP_DEBUG_TRACE
, "<==backsql_free_db_conn()\n", 0, 0, 0 );
477 static void *backsql_db_conn_dummy
;
480 backsql_db_conn_keyfree(
484 backsql_close_db_handle( (SQLHDBC
)data
);
488 backsql_get_db_conn( Operation
*op
, SQLHDBC
*dbhp
)
490 backsql_info
*bi
= (backsql_info
*)op
->o_bd
->be_private
;
491 int rc
= LDAP_SUCCESS
;
492 SQLHDBC dbh
= SQL_NULL_HDBC
;
494 Debug( LDAP_DEBUG_TRACE
, "==>backsql_get_db_conn()\n", 0, 0, 0 );
496 assert( dbhp
!= NULL
);
497 *dbhp
= SQL_NULL_HDBC
;
499 if ( op
->o_threadctx
) {
502 ldap_pvt_thread_pool_getkey( op
->o_threadctx
,
503 &backsql_db_conn_dummy
, &data
, NULL
);
510 if ( dbh
== SQL_NULL_HDBC
) {
511 rc
= backsql_open_db_handle( bi
, &dbh
);
512 if ( rc
!= LDAP_SUCCESS
) {
516 if ( op
->o_threadctx
) {
520 ldap_pvt_thread_pool_setkey( op
->o_threadctx
,
521 &backsql_db_conn_dummy
, data
,
522 backsql_db_conn_keyfree
, NULL
, NULL
);
531 Debug( LDAP_DEBUG_TRACE
, "<==backsql_get_db_conn()\n", 0, 0, 0 );