1 /*-------
2  * Module:			environ.c
3  *
4  * Description:		This module contains routines related to
5  *					the environment, such as storing connection handles,
6  *					and returning errors.
7  *
8  * Classes:			EnvironmentClass (Functions prefix: "EN_")
9  *
10  * API functions:	SQLAllocEnv, SQLFreeEnv, SQLError
11  *
12  * Comments:		See "readme.txt" for copyright and license information.
13  *-------
14  */
15 
16 #include "environ.h"
17 #include "misc.h"
18 
19 #include "connection.h"
20 #include "dlg_specific.h"
21 #include "statement.h"
22 #include <stdlib.h>
23 #include <string.h>
24 #include "pgapifunc.h"
25 #ifdef	WIN32
26 #include <winsock2.h>
27 #endif /* WIN32 */
28 #include "loadlib.h"
29 
30 
31 /* The one instance of the handles */
32 static int conns_count = 0;
33 static ConnectionClass **conns = NULL;
34 
35 #if defined(WIN_MULTITHREAD_SUPPORT)
36 CRITICAL_SECTION	conns_cs;
37 CRITICAL_SECTION	common_cs; /* commonly used for short term blocking */
38 CRITICAL_SECTION	common_lcs; /* commonly used for not necessarily short term blocking */
39 #elif defined(POSIX_MULTITHREAD_SUPPORT)
40 pthread_mutex_t     conns_cs;
41 pthread_mutex_t     common_cs;
42 pthread_mutex_t     common_lcs;
43 #endif /* WIN_MULTITHREAD_SUPPORT */
44 
shortterm_common_lock(void)45 void	shortterm_common_lock(void)
46 {
47 	ENTER_COMMON_CS;
48 }
shortterm_common_unlock(void)49 void	shortterm_common_unlock(void)
50 {
51 	LEAVE_COMMON_CS;
52 }
53 
getConnCount(void)54 int	getConnCount(void)
55 {
56 	return conns_count;
57 }
getConnList(void)58 ConnectionClass * const *getConnList(void)
59 {
60 	return conns;
61 }
62 
63 RETCODE		SQL_API
PGAPI_AllocEnv(HENV * phenv)64 PGAPI_AllocEnv(HENV * phenv)
65 {
66 	CSTR func = "PGAPI_AllocEnv";
67 	SQLRETURN	ret = SQL_SUCCESS;
68 
69 	MYLOG(0, "entering\n");
70 
71 	/*
72 	 * For systems on which none of the constructor-making
73 	 * techniques in psqlodbc.c work:
74 	 * It's ok to call initialize_global_cs() twice.
75 	 */
76 	{
77 		initialize_global_cs();
78 	}
79 
80 	*phenv = (HENV) EN_Constructor();
81 	if (!*phenv)
82 	{
83 		*phenv = SQL_NULL_HENV;
84 		EN_log_error(func, "Error allocating environment", NULL);
85 		ret = SQL_ERROR;
86 	}
87 
88 	MYLOG(0, "leaving phenv=%p\n", *phenv);
89 	return ret;
90 }
91 
92 
93 RETCODE		SQL_API
PGAPI_FreeEnv(HENV henv)94 PGAPI_FreeEnv(HENV henv)
95 {
96 	CSTR func = "PGAPI_FreeEnv";
97 	SQLRETURN	ret = SQL_SUCCESS;
98 	EnvironmentClass *env = (EnvironmentClass *) henv;
99 
100 	MYLOG(0, "entering env=%p\n", env);
101 
102 	if (env && EN_Destructor(env))
103 	{
104 		MYLOG(0, "   ok\n");
105 		goto cleanup;
106 	}
107 
108 	ret = SQL_ERROR;
109 	EN_log_error(func, "Error freeing environment", NULL);
110 cleanup:
111 	return ret;
112 }
113 
114 #define	SIZEOF_SQLSTATE	6
115 
116 static void
pg_sqlstate_set(const EnvironmentClass * env,UCHAR * szSqlState,const char * ver3str,const char * ver2str)117 pg_sqlstate_set(const EnvironmentClass *env, UCHAR *szSqlState, const char *ver3str, const char *ver2str)
118 {
119 	strncpy_null((char *) szSqlState, EN_is_odbc3(env) ? ver3str : ver2str, SIZEOF_SQLSTATE);
120 }
121 
122 PG_ErrorInfo *
ER_Constructor(SDWORD errnumber,const char * msg)123 ER_Constructor(SDWORD errnumber, const char *msg)
124 {
125 	PG_ErrorInfo	*error;
126 	ssize_t		aladd, errsize;
127 
128 	if (DESC_OK == errnumber)
129 		return NULL;
130 	if (msg)
131 	{
132 		errsize = strlen(msg);
133 		aladd = errsize - sizeof(error->__error_message) + 1;
134 		if (aladd < 0)
135 			aladd = 0;
136 	}
137 	else
138 	{
139 		errsize = -1;
140 		aladd = 0;
141 	}
142 	error = (PG_ErrorInfo *) malloc(sizeof(PG_ErrorInfo) + aladd);
143 	if (error)
144 	{
145 		memset(error, 0, sizeof(PG_ErrorInfo));
146 		error->status = errnumber;
147 		error->errorsize = (Int2) errsize;
148 		if (errsize > 0)
149 			memcpy(error->__error_message, msg, errsize);
150 		error->__error_message[errsize] = '\0';
151 		error->recsize = -1;
152 	}
153 	return error;
154 }
155 
156 void
ER_Destructor(PG_ErrorInfo * self)157 ER_Destructor(PG_ErrorInfo *self)
158 {
159 	free(self);
160 }
161 
162 PG_ErrorInfo *
ER_Dup(const PG_ErrorInfo * self)163 ER_Dup(const PG_ErrorInfo *self)
164 {
165 	PG_ErrorInfo	*new;
166 	Int4		alsize;
167 
168 	if (!self)
169 		return NULL;
170 	alsize = sizeof(PG_ErrorInfo);
171 	if (self->errorsize  > 0)
172 		alsize += self->errorsize;
173 	new = (PG_ErrorInfo *) malloc(alsize);
174 	if (new)
175 		memcpy(new, self, alsize);
176 
177 	return new;
178 }
179 
180 #define	DRVMNGRDIV	511
181 /*		Returns the next SQL error information. */
182 RETCODE		SQL_API
ER_ReturnError(PG_ErrorInfo * pgerror,SQLSMALLINT RecNumber,SQLCHAR * szSqlState,SQLINTEGER * pfNativeError,SQLCHAR * szErrorMsg,SQLSMALLINT cbErrorMsgMax,SQLSMALLINT * pcbErrorMsg,UWORD flag)183 ER_ReturnError(PG_ErrorInfo *pgerror,
184 			   SQLSMALLINT	RecNumber,
185 			   SQLCHAR * szSqlState,
186 			   SQLINTEGER * pfNativeError,
187 			   SQLCHAR * szErrorMsg,
188 			   SQLSMALLINT cbErrorMsgMax,
189 			   SQLSMALLINT * pcbErrorMsg,
190 			   UWORD flag)
191 {
192 	/* CC: return an error of a hstmt  */
193 	PG_ErrorInfo	*error;
194 	BOOL		partial_ok = ((flag & PODBC_ALLOW_PARTIAL_EXTRACT) != 0);
195 	const char	*msg;
196 	SWORD		msglen, stapos, wrtlen, pcblen;
197 
198 	if (!pgerror)
199 		return SQL_NO_DATA_FOUND;
200 	error = pgerror;
201 	msg = error->__error_message;
202 	MYLOG(0, "entering status = %d, msg = #%s#\n", error->status, msg);
203 	msglen = (SQLSMALLINT) strlen(msg);
204 	/*
205 	 *	Even though an application specifies a larger error message
206 	 *	buffer, the driver manager changes it silently.
207 	 *	Therefore we divide the error message into ...
208 	 */
209 	if (error->recsize < 0)
210 	{
211 		if (cbErrorMsgMax > 0)
212 			error->recsize = cbErrorMsgMax - 1; /* apply the first request */
213 		else
214 			error->recsize = DRVMNGRDIV;
215 	}
216 	else if (1 == RecNumber && cbErrorMsgMax > 0)
217 		error->recsize = cbErrorMsgMax - 1;
218 	if (RecNumber < 0)
219 	{
220 		if (0 == error->errorpos)
221 			RecNumber = 1;
222 		else
223 			RecNumber = 2 + (error->errorpos - 1) / error->recsize;
224 	}
225 	stapos = (RecNumber - 1) * error->recsize;
226 	if (stapos > msglen)
227 		return SQL_NO_DATA_FOUND;
228 	pcblen = wrtlen = msglen - stapos;
229 	if (pcblen > error->recsize)
230 		pcblen = error->recsize;
231 	if (0 == cbErrorMsgMax)
232 		wrtlen = 0;
233 	else if (wrtlen >= cbErrorMsgMax)
234 	{
235 		if (partial_ok)
236 			wrtlen = cbErrorMsgMax - 1;
237 		else if (cbErrorMsgMax <= error->recsize)
238 			wrtlen = cbErrorMsgMax - 1;
239 		else
240 			wrtlen = error->recsize;
241 	}
242 	if (wrtlen > pcblen)
243 		wrtlen = pcblen;
244 	if (NULL != pcbErrorMsg)
245 		*pcbErrorMsg = pcblen;
246 
247 	if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
248 	{
249 		memcpy(szErrorMsg, msg + stapos, wrtlen);
250 		szErrorMsg[wrtlen] = '\0';
251 	}
252 
253 	if (NULL != pfNativeError)
254 		*pfNativeError = error->status;
255 
256 	if (NULL != szSqlState)
257 		strncpy_null((char *) szSqlState, error->sqlstate, 6);
258 
259 	MYLOG(0, "	     szSqlState = '%s',len=%d, szError='%s'\n", szSqlState, pcblen, szErrorMsg);
260 	if (wrtlen < pcblen)
261 		return SQL_SUCCESS_WITH_INFO;
262 	else
263 		return SQL_SUCCESS;
264 }
265 
266 
267 RETCODE		SQL_API
PGAPI_ConnectError(HDBC hdbc,SQLSMALLINT RecNumber,SQLCHAR * szSqlState,SQLINTEGER * pfNativeError,SQLCHAR * szErrorMsg,SQLSMALLINT cbErrorMsgMax,SQLSMALLINT * pcbErrorMsg,UWORD flag)268 PGAPI_ConnectError(HDBC hdbc,
269 				   SQLSMALLINT	RecNumber,
270 				   SQLCHAR * szSqlState,
271 				   SQLINTEGER * pfNativeError,
272 				   SQLCHAR * szErrorMsg,
273 				   SQLSMALLINT cbErrorMsgMax,
274 				   SQLSMALLINT * pcbErrorMsg,
275 				   UWORD flag)
276 {
277 	ConnectionClass *conn = (ConnectionClass *) hdbc;
278 	EnvironmentClass *env = (EnvironmentClass *) conn->henv;
279 	char		*msg;
280 	int		status;
281 	BOOL	once_again = FALSE;
282 	ssize_t		msglen;
283 
284 	MYLOG(0, "entering hdbc=%p <%d>\n", hdbc, cbErrorMsgMax);
285 	if (RecNumber != 1 && RecNumber != -1)
286 		return SQL_NO_DATA_FOUND;
287 	if (cbErrorMsgMax < 0)
288 		return SQL_ERROR;
289 	if (CONN_EXECUTING == conn->status || !CC_get_error(conn, &status, &msg) || NULL == msg)
290 	{
291 		MYLOG(0, "CC_Get_error returned nothing.\n");
292 		if (NULL != szSqlState)
293 			strncpy_null((char *) szSqlState, "00000", SIZEOF_SQLSTATE);
294 		if (NULL != pcbErrorMsg)
295 			*pcbErrorMsg = 0;
296 		if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
297 			szErrorMsg[0] = '\0';
298 
299 		return SQL_NO_DATA_FOUND;
300 	}
301 	MYLOG(0, "CC_get_error: status = %d, msg = #%s#\n", status, msg);
302 
303 	msglen = strlen(msg);
304 	if (NULL != pcbErrorMsg)
305 	{
306 		*pcbErrorMsg = (SQLSMALLINT) msglen;
307 		if (cbErrorMsgMax == 0)
308 			once_again = TRUE;
309 		else if (msglen >= cbErrorMsgMax)
310 			*pcbErrorMsg = cbErrorMsgMax - 1;
311 	}
312 	if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
313 		strncpy_null((char *) szErrorMsg, msg, cbErrorMsgMax);
314 	if (NULL != pfNativeError)
315 		*pfNativeError = status;
316 
317 	if (NULL != szSqlState)
318 	{
319 		if (conn->sqlstate[0])
320 			strncpy_null((char *) szSqlState, conn->sqlstate, SIZEOF_SQLSTATE);
321 		else
322 		switch (status)
323 		{
324 			case CONN_OPTION_VALUE_CHANGED:
325 				pg_sqlstate_set(env, szSqlState, "01S02", "01S02");
326 				break;
327 			case CONN_TRUNCATED:
328 				pg_sqlstate_set(env, szSqlState, "01004", "01004");
329 				/* data truncated */
330 				break;
331 			case CONN_INIREAD_ERROR:
332 				pg_sqlstate_set(env, szSqlState, "IM002", "IM002");
333 				/* data source not found */
334 				break;
335 			case CONNECTION_SERVER_NOT_REACHED:
336 			case CONN_OPENDB_ERROR:
337 				pg_sqlstate_set(env, szSqlState, "08001", "08001");
338 				/* unable to connect to data source */
339 				break;
340 			case CONN_INVALID_AUTHENTICATION:
341 			case CONN_AUTH_TYPE_UNSUPPORTED:
342 				pg_sqlstate_set(env, szSqlState, "28000", "28000");
343 				break;
344 			case CONN_STMT_ALLOC_ERROR:
345 				pg_sqlstate_set(env, szSqlState, "HY001", "S1001");
346 				/* memory allocation failure */
347 				break;
348 			case CONN_IN_USE:
349 				pg_sqlstate_set(env, szSqlState, "HY000", "S1000");
350 				/* general error */
351 				break;
352 			case CONN_UNSUPPORTED_OPTION:
353 				pg_sqlstate_set(env, szSqlState, "HYC00", "IM001");
354 				/* driver does not support this function */
355 				break;
356 			case CONN_INVALID_ARGUMENT_NO:
357 				pg_sqlstate_set(env, szSqlState, "HY009", "S1009");
358 				/* invalid argument value */
359 				break;
360 			case CONN_TRANSACT_IN_PROGRES:
361 				pg_sqlstate_set(env, szSqlState, "HY011", "S1011");
362 				break;
363 			case CONN_NO_MEMORY_ERROR:
364 				pg_sqlstate_set(env, szSqlState, "HY001", "S1001");
365 				break;
366 			case CONN_NOT_IMPLEMENTED_ERROR:
367 				pg_sqlstate_set(env, szSqlState, "HYC00", "S1C00");
368 				break;
369 			case CONN_ILLEGAL_TRANSACT_STATE:
370 				pg_sqlstate_set(env, szSqlState, "25000", "S1010");
371 				break;
372 			case CONN_VALUE_OUT_OF_RANGE:
373 				pg_sqlstate_set(env, szSqlState, "HY019", "22003");
374 				break;
375 			case CONNECTION_COULD_NOT_SEND:
376 			case CONNECTION_COULD_NOT_RECEIVE:
377 			case CONNECTION_COMMUNICATION_ERROR:
378 			case CONNECTION_NO_RESPONSE:
379 				pg_sqlstate_set(env, szSqlState, "08S01", "08S01");
380 				break;
381 			default:
382 				pg_sqlstate_set(env, szSqlState, "HY000", "S1000");
383 				/* general error */
384 				break;
385 		}
386 	}
387 
388 	MYLOG(0, "	     szSqlState = '%s',len=" FORMAT_SSIZE_T ", szError='%s'\n", szSqlState ? (char *) szSqlState : PRINT_NULL, msglen, szErrorMsg ? (char *) szErrorMsg : PRINT_NULL);
389 	if (once_again)
390 	{
391 		CC_set_errornumber(conn, status);
392 		return SQL_SUCCESS_WITH_INFO;
393 	}
394 	else
395 		return SQL_SUCCESS;
396 }
397 
398 RETCODE		SQL_API
PGAPI_EnvError(HENV henv,SQLSMALLINT RecNumber,SQLCHAR * szSqlState,SQLINTEGER * pfNativeError,SQLCHAR * szErrorMsg,SQLSMALLINT cbErrorMsgMax,SQLSMALLINT * pcbErrorMsg,UWORD flag)399 PGAPI_EnvError(HENV henv,
400 			   SQLSMALLINT	RecNumber,
401 			   SQLCHAR * szSqlState,
402 			   SQLINTEGER * pfNativeError,
403 			   SQLCHAR * szErrorMsg,
404 			   SQLSMALLINT cbErrorMsgMax,
405 			   SQLSMALLINT * pcbErrorMsg,
406 			   UWORD flag)
407 {
408 	EnvironmentClass *env = (EnvironmentClass *) henv;
409 	char		*msg = NULL;
410 	int		status;
411 
412 	MYLOG(0, "entering henv=%p <%d>\n", henv, cbErrorMsgMax);
413 	if (RecNumber != 1 && RecNumber != -1)
414 		return SQL_NO_DATA_FOUND;
415 	if (cbErrorMsgMax < 0)
416 		return SQL_ERROR;
417 	if (!EN_get_error(env, &status, &msg) || NULL == msg)
418 	{
419 		MYLOG(0, "EN_get_error: msg = #%s#\n", msg);
420 
421 		if (NULL != szSqlState)
422 			pg_sqlstate_set(env, szSqlState, "00000", "00000");
423 		if (NULL != pcbErrorMsg)
424 			*pcbErrorMsg = 0;
425 		if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
426 			szErrorMsg[0] = '\0';
427 
428 		return SQL_NO_DATA_FOUND;
429 	}
430 	MYLOG(0, "EN_get_error: status = %d, msg = #%s#\n", status, msg);
431 
432 	if (NULL != pcbErrorMsg)
433 		*pcbErrorMsg = (SQLSMALLINT) strlen(msg);
434 	if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
435 		strncpy_null((char *) szErrorMsg, msg, cbErrorMsgMax);
436 	if (NULL != pfNativeError)
437 		*pfNativeError = status;
438 
439 	if (szSqlState)
440 	{
441 		switch (status)
442 		{
443 			case ENV_ALLOC_ERROR:
444 				/* memory allocation failure */
445 				pg_sqlstate_set(env, szSqlState, "HY001", "S1001");
446 				break;
447 			default:
448 				pg_sqlstate_set(env, szSqlState, "HY000", "S1000");
449 				/* general error */
450 				break;
451 		}
452 	}
453 
454 	return SQL_SUCCESS;
455 }
456 
457 
458 /*
459  * EnvironmentClass implementation
460  */
461 EnvironmentClass *
EN_Constructor(void)462 EN_Constructor(void)
463 {
464 	EnvironmentClass *rv = NULL;
465 #ifdef WIN32
466 	WORD		wVersionRequested;
467 	WSADATA		wsaData;
468 	const int	major = 2, minor = 2;
469 
470 	/* Load the WinSock Library */
471 	wVersionRequested = MAKEWORD(major, minor);
472 
473 	if (WSAStartup(wVersionRequested, &wsaData))
474 	{
475 		MYLOG(0, " WSAStartup error\n");
476 		return rv;
477 	}
478 	/* Verify that this is the minimum version of WinSock */
479 	if (LOBYTE(wsaData.wVersion) >= 1 &&
480 	    (LOBYTE(wsaData.wVersion) >= 2 ||
481 	     HIBYTE(wsaData.wVersion) >= 1))
482 		;
483 	else
484 	{
485 		MYLOG(0, " WSAStartup version=(%d,%d)\n",
486 			LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
487 		goto cleanup;
488 	}
489 #endif /* WIN32 */
490 
491 	rv = (EnvironmentClass *) malloc(sizeof(EnvironmentClass));
492 	if (NULL == rv)
493 	{
494 		MYLOG(0, " malloc error\n");
495 		goto cleanup;
496 	}
497 	rv->errormsg = 0;
498 	rv->errornumber = 0;
499 	rv->flag = 0;
500 	INIT_ENV_CS(rv);
501 cleanup:
502 #ifdef WIN32
503 	if (NULL == rv)
504 	{
505 		WSACleanup();
506 	}
507 #endif /* WIN32 */
508 
509 	return rv;
510 }
511 
512 
513 char
EN_Destructor(EnvironmentClass * self)514 EN_Destructor(EnvironmentClass *self)
515 {
516 	int		lf, nullcnt;
517 	char		rv = 1;
518 
519 	MYLOG(0, "entering self=%p\n", self);
520 	if (!self)
521 		return 0;
522 
523 	/*
524 	 * the error messages are static strings distributed throughout the
525 	 * source--they should not be freed
526 	 */
527 
528 	/* Free any connections belonging to this environment */
529 	ENTER_CONNS_CS;
530 	for (lf = 0, nullcnt = 0; lf < conns_count; lf++)
531 	{
532 		if (NULL == conns[lf])
533 			nullcnt++;
534 		else if (conns[lf]->henv == self)
535 		{
536 			if (CC_Destructor(conns[lf]))
537 				conns[lf] = NULL;
538 			else
539 				rv = 0;
540 			nullcnt++;
541 		}
542 	}
543 	if (conns && nullcnt >= conns_count)
544 	{
545 		MYLOG(0, "clearing conns count=%d\n", conns_count);
546 		free(conns);
547 		conns = NULL;
548 		conns_count = 0;
549 	}
550 	LEAVE_CONNS_CS;
551 	DELETE_ENV_CS(self);
552 	free(self);
553 
554 #ifdef WIN32
555 	WSACleanup();
556 #endif
557 	MYLOG(0, "leaving rv=%d\n", rv);
558 #ifdef	_MEMORY_DEBUG_
559 	debug_memory_check();
560 #endif   /* _MEMORY_DEBUG_ */
561 	return rv;
562 }
563 
564 
565 char
EN_get_error(EnvironmentClass * self,int * number,char ** message)566 EN_get_error(EnvironmentClass *self, int *number, char **message)
567 {
568 	if (self && self->errormsg && self->errornumber)
569 	{
570 		*message = self->errormsg;
571 		*number = self->errornumber;
572 		self->errormsg = 0;
573 		self->errornumber = 0;
574 		return 1;
575 	}
576 	else
577 		return 0;
578 }
579 
580 #define	INIT_CONN_COUNT	128
581 
582 char
EN_add_connection(EnvironmentClass * self,ConnectionClass * conn)583 EN_add_connection(EnvironmentClass *self, ConnectionClass *conn)
584 {
585 	int	i, alloc;
586 	ConnectionClass	**newa;
587 	char	ret = FALSE;
588 
589 	MYLOG(0, "entering self = %p, conn = %p\n", self, conn);
590 
591 	ENTER_CONNS_CS;
592 	for (i = 0; i < conns_count; i++)
593 	{
594 		if (!conns[i])
595 		{
596 			conn->henv = self;
597 			conns[i] = conn;
598 			ret = TRUE;
599 			MYLOG(0, "       added at i=%d, conn->henv = %p, conns[i]->henv = %p\n", i, conn->henv, conns[i]->henv);
600 			goto cleanup;
601 		}
602 	}
603 	if (conns_count > 0)
604 		alloc = 2 * conns_count;
605 	else
606 		alloc = INIT_CONN_COUNT;
607 	if (newa = (ConnectionClass **) realloc(conns, alloc * sizeof(ConnectionClass *)), NULL == newa)
608 		goto cleanup;
609 	conn->henv = self;
610 	newa[conns_count] = conn;
611 	conns = newa;
612 	ret = TRUE;
613 	MYLOG(0, "       added at %d, conn->henv = %p, conns[%d]->henv = %p\n", conns_count, conn->henv, conns_count, conns[conns_count]->henv);
614 	for (i = conns_count + 1; i < alloc; i++)
615 		conns[i] = NULL;
616 	conns_count = alloc;
617 cleanup:
618 	LEAVE_CONNS_CS;
619 	return ret;
620 }
621 
622 
623 char
EN_remove_connection(EnvironmentClass * self,ConnectionClass * conn)624 EN_remove_connection(EnvironmentClass *self, ConnectionClass *conn)
625 {
626 	int			i;
627 
628 	for (i = 0; i < conns_count; i++)
629 		if (conns[i] == conn && conns[i]->status != CONN_EXECUTING)
630 		{
631 			ENTER_CONNS_CS;
632 			conns[i] = NULL;
633 			LEAVE_CONNS_CS;
634 			return TRUE;
635 		}
636 
637 	return FALSE;
638 }
639 
640 
641 void
EN_log_error(const char * func,char * desc,EnvironmentClass * self)642 EN_log_error(const char *func, char *desc, EnvironmentClass *self)
643 {
644 	if (self)
645 		MYLOG(0, "ENVIRON ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg);
646 	else
647 		MYLOG(0, "INVALID ENVIRON HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
648 }
649