LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/ecpglib - misc.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 170 241 70.5 %
Date: 2024-11-21 08:14:44 Functions: 14 16 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* src/interfaces/ecpg/ecpglib/misc.c */
       2             : 
       3             : #define POSTGRES_ECPG_INTERNAL
       4             : #include "postgres_fe.h"
       5             : 
       6             : #include <limits.h>
       7             : #include <unistd.h>
       8             : 
       9             : #include "ecpg-pthread-win32.h"
      10             : #include "ecpgerrno.h"
      11             : #include "ecpglib.h"
      12             : #include "ecpglib_extern.h"
      13             : #include "ecpgtype.h"
      14             : #include "pg_config_paths.h"
      15             : #include "pgtypes_date.h"
      16             : #include "pgtypes_interval.h"
      17             : #include "pgtypes_numeric.h"
      18             : #include "pgtypes_timestamp.h"
      19             : #include "sqlca.h"
      20             : 
      21             : #ifndef LONG_LONG_MIN
      22             : #ifdef LLONG_MIN
      23             : #define LONG_LONG_MIN LLONG_MIN
      24             : #else
      25             : #define LONG_LONG_MIN LONGLONG_MIN
      26             : #endif                          /* LLONG_MIN */
      27             : #endif                          /* LONG_LONG_MIN */
      28             : 
      29             : bool        ecpg_internal_regression_mode = false;
      30             : 
      31             : static struct sqlca_t sqlca_init =
      32             : {
      33             :     {
      34             :         'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
      35             :     },
      36             :     sizeof(struct sqlca_t),
      37             :     0,
      38             :     {
      39             :         0,
      40             :         {
      41             :             0
      42             :         }
      43             :     },
      44             :     {
      45             :         'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '
      46             :     },
      47             :     {
      48             :         0, 0, 0, 0, 0, 0
      49             :     },
      50             :     {
      51             :         0, 0, 0, 0, 0, 0, 0, 0
      52             :     },
      53             :     {
      54             :         '0', '0', '0', '0', '0'
      55             :     }
      56             : };
      57             : 
      58             : static pthread_key_t sqlca_key;
      59             : static pthread_once_t sqlca_key_once = PTHREAD_ONCE_INIT;
      60             : 
      61             : static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
      62             : static pthread_mutex_t debug_init_mutex = PTHREAD_MUTEX_INITIALIZER;
      63             : static volatile int simple_debug = 0;
      64             : static FILE *debugstream = NULL;
      65             : 
      66             : void
      67     6417004 : ecpg_init_sqlca(struct sqlca_t *sqlca)
      68             : {
      69     6417004 :     memcpy((char *) sqlca, (char *) &sqlca_init, sizeof(struct sqlca_t));
      70     6417004 : }
      71             : 
      72             : bool
      73       15400 : ecpg_init(const struct connection *con, const char *connection_name, const int lineno)
      74             : {
      75       15400 :     struct sqlca_t *sqlca = ECPGget_sqlca();
      76             : 
      77       15400 :     if (sqlca == NULL)
      78             :     {
      79           0 :         ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY,
      80             :                    NULL);
      81           0 :         return false;
      82             :     }
      83             : 
      84       15400 :     ecpg_init_sqlca(sqlca);
      85       15400 :     if (con == NULL)
      86             :     {
      87          86 :         ecpg_raise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
      88          24 :                    connection_name ? connection_name : ecpg_gettext("NULL"));
      89          62 :         return false;
      90             :     }
      91             : 
      92       15338 :     return true;
      93             : }
      94             : 
      95             : static void
      96         272 : ecpg_sqlca_key_destructor(void *arg)
      97             : {
      98         272 :     free(arg);                  /* sqlca structure allocated in ECPGget_sqlca */
      99         272 : }
     100             : 
     101             : static void
     102         254 : ecpg_sqlca_key_init(void)
     103             : {
     104         254 :     pthread_key_create(&sqlca_key, ecpg_sqlca_key_destructor);
     105         254 : }
     106             : 
     107             : struct sqlca_t *
     108    12872416 : ECPGget_sqlca(void)
     109             : {
     110             :     struct sqlca_t *sqlca;
     111             : 
     112    12872416 :     pthread_once(&sqlca_key_once, ecpg_sqlca_key_init);
     113             : 
     114    12872416 :     sqlca = pthread_getspecific(sqlca_key);
     115    12872416 :     if (sqlca == NULL)
     116             :     {
     117         518 :         sqlca = malloc(sizeof(struct sqlca_t));
     118         518 :         if (sqlca == NULL)
     119           0 :             return NULL;
     120         518 :         ecpg_init_sqlca(sqlca);
     121         518 :         pthread_setspecific(sqlca_key, sqlca);
     122             :     }
     123    12872416 :     return sqlca;
     124             : }
     125             : 
     126             : bool
     127           0 : ECPGstatus(int lineno, const char *connection_name)
     128             : {
     129           0 :     struct connection *con = ecpg_get_connection(connection_name);
     130             : 
     131           0 :     if (!ecpg_init(con, connection_name, lineno))
     132           0 :         return false;
     133             : 
     134             :     /* are we connected? */
     135           0 :     if (con->connection == NULL)
     136             :     {
     137           0 :         ecpg_raise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, con->name);
     138           0 :         return false;
     139             :     }
     140             : 
     141           0 :     return true;
     142             : }
     143             : 
     144             : PGTransactionStatusType
     145           0 : ECPGtransactionStatus(const char *connection_name)
     146             : {
     147             :     const struct connection *con;
     148             : 
     149           0 :     con = ecpg_get_connection(connection_name);
     150           0 :     if (con == NULL)
     151             :     {
     152             :         /* transaction status is unknown */
     153           0 :         return PQTRANS_UNKNOWN;
     154             :     }
     155             : 
     156           0 :     return PQtransactionStatus(con->connection);
     157             : }
     158             : 
     159             : bool
     160         492 : ECPGtrans(int lineno, const char *connection_name, const char *transaction)
     161             : {
     162             :     PGresult   *res;
     163         492 :     struct connection *con = ecpg_get_connection(connection_name);
     164             : 
     165         492 :     if (!ecpg_init(con, connection_name, lineno))
     166           0 :         return false;
     167             : 
     168         492 :     ecpg_log("ECPGtrans on line %d: action \"%s\"; connection \"%s\"\n", lineno, transaction, con ? con->name : "null");
     169             : 
     170             :     /* if we have no connection we just simulate the command */
     171         492 :     if (con && con->connection)
     172             :     {
     173             :         /*
     174             :          * If we got a transaction command but have no open transaction, we
     175             :          * have to start one, unless we are in autocommit, where the
     176             :          * developers have to take care themselves. However, if the command is
     177             :          * a begin statement, we just execute it once. And if the command is
     178             :          * commit or rollback prepared, we don't execute it.
     179             :          */
     180         492 :         if (PQtransactionStatus(con->connection) == PQTRANS_IDLE &&
     181         108 :             !con->autocommit &&
     182          96 :             strncmp(transaction, "begin", 5) != 0 &&
     183           4 :             strncmp(transaction, "start", 5) != 0 &&
     184           4 :             strncmp(transaction, "commit prepared", 15) != 0 &&
     185           0 :             strncmp(transaction, "rollback prepared", 17) != 0)
     186             :         {
     187           0 :             res = PQexec(con->connection, "begin transaction");
     188           0 :             if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
     189           0 :                 return false;
     190           0 :             PQclear(res);
     191             :         }
     192             : 
     193         492 :         res = PQexec(con->connection, transaction);
     194         492 :         if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
     195           0 :             return false;
     196         492 :         PQclear(res);
     197             :     }
     198             : 
     199         492 :     return true;
     200             : }
     201             : 
     202             : 
     203             : void
     204         234 : ECPGdebug(int n, FILE *dbgs)
     205             : {
     206             :     /* Interlock against concurrent executions of ECPGdebug() */
     207         234 :     pthread_mutex_lock(&debug_init_mutex);
     208             : 
     209             :     /* Prevent ecpg_log() from printing while we change settings */
     210         234 :     pthread_mutex_lock(&debug_mutex);
     211             : 
     212         234 :     if (n > 100)
     213             :     {
     214         234 :         ecpg_internal_regression_mode = true;
     215         234 :         simple_debug = n - 100;
     216             :     }
     217             :     else
     218           0 :         simple_debug = n;
     219             : 
     220         234 :     debugstream = dbgs;
     221             : 
     222             :     /* We must release debug_mutex before invoking ecpg_log() ... */
     223         234 :     pthread_mutex_unlock(&debug_mutex);
     224             : 
     225             :     /* ... but keep holding debug_init_mutex to avoid racy printout */
     226         234 :     ecpg_log("ECPGdebug: set to %d\n", simple_debug);
     227             : 
     228         234 :     pthread_mutex_unlock(&debug_init_mutex);
     229         234 : }
     230             : 
     231             : void
     232       58572 : ecpg_log(const char *format,...)
     233             : {
     234             :     va_list     ap;
     235             :     const char *intl_format;
     236             :     int         bufsize;
     237             :     char       *fmt;
     238             :     struct sqlca_t *sqlca;
     239             : 
     240             :     /*
     241             :      * For performance reasons, inspect simple_debug without taking the mutex.
     242             :      * This could be problematic if fetching an int isn't atomic, but we
     243             :      * assume that it is in many other places too.
     244             :      */
     245       58572 :     if (!simple_debug)
     246       45084 :         return;
     247             : 
     248             :     /* localize the error message string */
     249       13488 :     intl_format = ecpg_gettext(format);
     250             : 
     251             :     /*
     252             :      * Insert PID into the format, unless ecpg_internal_regression_mode is set
     253             :      * (regression tests want unchanging output).
     254             :      */
     255       13488 :     bufsize = strlen(intl_format) + 100;
     256       13488 :     fmt = (char *) malloc(bufsize);
     257       13488 :     if (fmt == NULL)
     258           0 :         return;
     259             : 
     260       13488 :     if (ecpg_internal_regression_mode)
     261       13488 :         snprintf(fmt, bufsize, "[NO_PID]: %s", intl_format);
     262             :     else
     263           0 :         snprintf(fmt, bufsize, "[%d]: %s", (int) getpid(), intl_format);
     264             : 
     265       13488 :     sqlca = ECPGget_sqlca();
     266             : 
     267       13488 :     pthread_mutex_lock(&debug_mutex);
     268             : 
     269             :     /* Now that we hold the mutex, recheck simple_debug */
     270       13488 :     if (simple_debug)
     271             :     {
     272       13488 :         va_start(ap, format);
     273       13488 :         vfprintf(debugstream, fmt, ap);
     274       13488 :         va_end(ap);
     275             : 
     276             :         /* dump out internal sqlca variables */
     277       13488 :         if (ecpg_internal_regression_mode && sqlca != NULL)
     278             :         {
     279       13488 :             fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
     280       13488 :                     sqlca->sqlcode, sqlca->sqlstate);
     281             :         }
     282             : 
     283       13488 :         fflush(debugstream);
     284             :     }
     285             : 
     286       13488 :     pthread_mutex_unlock(&debug_mutex);
     287             : 
     288       13488 :     free(fmt);
     289             : }
     290             : 
     291             : void
     292        7140 : ECPGset_noind_null(enum ECPGttype type, void *ptr)
     293             : {
     294        7140 :     switch (type)
     295             :     {
     296        3780 :         case ECPGt_char:
     297             :         case ECPGt_unsigned_char:
     298             :         case ECPGt_string:
     299        3780 :             *((char *) ptr) = '\0';
     300        3780 :             break;
     301           8 :         case ECPGt_short:
     302             :         case ECPGt_unsigned_short:
     303           8 :             *((short int *) ptr) = SHRT_MIN;
     304           8 :             break;
     305           8 :         case ECPGt_int:
     306             :         case ECPGt_unsigned_int:
     307           8 :             *((int *) ptr) = INT_MIN;
     308           8 :             break;
     309          20 :         case ECPGt_long:
     310             :         case ECPGt_unsigned_long:
     311             :         case ECPGt_date:
     312          20 :             *((long *) ptr) = LONG_MIN;
     313          20 :             break;
     314           0 :         case ECPGt_long_long:
     315             :         case ECPGt_unsigned_long_long:
     316           0 :             *((long long *) ptr) = LONG_LONG_MIN;
     317           0 :             break;
     318           8 :         case ECPGt_float:
     319           8 :             memset((char *) ptr, 0xff, sizeof(float));
     320           8 :             break;
     321          16 :         case ECPGt_double:
     322          16 :             memset((char *) ptr, 0xff, sizeof(double));
     323          16 :             break;
     324           0 :         case ECPGt_varchar:
     325           0 :             *(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
     326           0 :             ((struct ECPGgeneric_varchar *) ptr)->len = 0;
     327           0 :             break;
     328           0 :         case ECPGt_bytea:
     329           0 :             ((struct ECPGgeneric_bytea *) ptr)->len = 0;
     330           0 :             break;
     331        3276 :         case ECPGt_decimal:
     332        3276 :             memset((char *) ptr, 0, sizeof(decimal));
     333        3276 :             ((decimal *) ptr)->sign = NUMERIC_NULL;
     334        3276 :             break;
     335           8 :         case ECPGt_numeric:
     336           8 :             memset((char *) ptr, 0, sizeof(numeric));
     337           8 :             ((numeric *) ptr)->sign = NUMERIC_NULL;
     338           8 :             break;
     339           0 :         case ECPGt_interval:
     340           0 :             memset((char *) ptr, 0xff, sizeof(interval));
     341           0 :             break;
     342          12 :         case ECPGt_timestamp:
     343          12 :             memset((char *) ptr, 0xff, sizeof(timestamp));
     344          12 :             break;
     345           4 :         default:
     346           4 :             break;
     347             :     }
     348        7140 : }
     349             : 
     350             : static bool
     351          44 : _check(const unsigned char *ptr, int length)
     352             : {
     353         236 :     for (length--; length >= 0; length--)
     354         208 :         if (ptr[length] != 0xff)
     355          16 :             return false;
     356             : 
     357          28 :     return true;
     358             : }
     359             : 
     360             : bool
     361       11052 : ECPGis_noind_null(enum ECPGttype type, const void *ptr)
     362             : {
     363       11052 :     switch (type)
     364             :     {
     365          80 :         case ECPGt_char:
     366             :         case ECPGt_unsigned_char:
     367             :         case ECPGt_string:
     368          80 :             if (*((const char *) ptr) == '\0')
     369           8 :                 return true;
     370          72 :             break;
     371          16 :         case ECPGt_short:
     372             :         case ECPGt_unsigned_short:
     373          16 :             if (*((const short int *) ptr) == SHRT_MIN)
     374           8 :                 return true;
     375           8 :             break;
     376          72 :         case ECPGt_int:
     377             :         case ECPGt_unsigned_int:
     378          72 :             if (*((const int *) ptr) == INT_MIN)
     379           8 :                 return true;
     380          64 :             break;
     381          72 :         case ECPGt_long:
     382             :         case ECPGt_unsigned_long:
     383             :         case ECPGt_date:
     384          72 :             if (*((const long *) ptr) == LONG_MIN)
     385          20 :                 return true;
     386          52 :             break;
     387           0 :         case ECPGt_long_long:
     388             :         case ECPGt_unsigned_long_long:
     389           0 :             if (*((const long long *) ptr) == LONG_LONG_MIN)
     390           0 :                 return true;
     391           0 :             break;
     392          16 :         case ECPGt_float:
     393          16 :             return _check(ptr, sizeof(float));
     394             :             break;
     395          16 :         case ECPGt_double:
     396          16 :             return _check(ptr, sizeof(double));
     397             :             break;
     398           0 :         case ECPGt_varchar:
     399           0 :             if (*(((const struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
     400           0 :                 return true;
     401           0 :             break;
     402           0 :         case ECPGt_bytea:
     403           0 :             if (((const struct ECPGgeneric_bytea *) ptr)->len == 0)
     404           0 :                 return true;
     405           0 :             break;
     406       10752 :         case ECPGt_decimal:
     407       10752 :             if (((const decimal *) ptr)->sign == NUMERIC_NULL)
     408        1040 :                 return true;
     409        9712 :             break;
     410           0 :         case ECPGt_numeric:
     411           0 :             if (((const numeric *) ptr)->sign == NUMERIC_NULL)
     412           0 :                 return true;
     413           0 :             break;
     414           0 :         case ECPGt_interval:
     415           0 :             return _check(ptr, sizeof(interval));
     416             :             break;
     417          12 :         case ECPGt_timestamp:
     418          12 :             return _check(ptr, sizeof(timestamp));
     419             :             break;
     420          16 :         default:
     421          16 :             break;
     422             :     }
     423             : 
     424        9924 :     return false;
     425             : }
     426             : 
     427             : #ifdef WIN32
     428             : 
     429             : int
     430             : pthread_mutex_init(pthread_mutex_t *mp, void *attr)
     431             : {
     432             :     mp->initstate = 0;
     433             :     return 0;
     434             : }
     435             : 
     436             : int
     437             : pthread_mutex_lock(pthread_mutex_t *mp)
     438             : {
     439             :     /* Initialize the csection if not already done */
     440             :     if (mp->initstate != 1)
     441             :     {
     442             :         LONG        istate;
     443             : 
     444             :         while ((istate = InterlockedExchange(&mp->initstate, 2)) == 2)
     445             :             Sleep(0);           /* wait, another thread is doing this */
     446             :         if (istate != 1)
     447             :             InitializeCriticalSection(&mp->csection);
     448             :         InterlockedExchange(&mp->initstate, 1);
     449             :     }
     450             :     EnterCriticalSection(&mp->csection);
     451             :     return 0;
     452             : }
     453             : 
     454             : int
     455             : pthread_mutex_unlock(pthread_mutex_t *mp)
     456             : {
     457             :     if (mp->initstate != 1)
     458             :         return EINVAL;
     459             :     LeaveCriticalSection(&mp->csection);
     460             :     return 0;
     461             : }
     462             : 
     463             : static pthread_mutex_t win32_pthread_once_lock = PTHREAD_MUTEX_INITIALIZER;
     464             : 
     465             : void
     466             : win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void))
     467             : {
     468             :     if (!*once)
     469             :     {
     470             :         pthread_mutex_lock(&win32_pthread_once_lock);
     471             :         if (!*once)
     472             :         {
     473             :             fn();
     474             :             *once = true;
     475             :         }
     476             :         pthread_mutex_unlock(&win32_pthread_once_lock);
     477             :     }
     478             : }
     479             : #endif                          /* WIN32 */
     480             : 
     481             : #ifdef ENABLE_NLS
     482             : 
     483             : char *
     484       13778 : ecpg_gettext(const char *msgid)
     485             : {
     486             :     /*
     487             :      * At least on Windows, there are gettext implementations that fail if
     488             :      * multiple threads call bindtextdomain() concurrently.  Use a mutex and
     489             :      * flag variable to ensure that we call it just once per process.  It is
     490             :      * not known that similar bugs exist on non-Windows platforms, but we
     491             :      * might as well do it the same way everywhere.
     492             :      */
     493             :     static volatile bool already_bound = false;
     494             :     static pthread_mutex_t binddomain_mutex = PTHREAD_MUTEX_INITIALIZER;
     495             : 
     496       13778 :     if (!already_bound)
     497             :     {
     498             :         /* dgettext() preserves errno, but bindtextdomain() doesn't */
     499             : #ifdef WIN32
     500             :         int         save_errno = GetLastError();
     501             : #else
     502         230 :         int         save_errno = errno;
     503             : #endif
     504             : 
     505         230 :         (void) pthread_mutex_lock(&binddomain_mutex);
     506             : 
     507         230 :         if (!already_bound)
     508             :         {
     509             :             const char *ldir;
     510             : 
     511             :             /*
     512             :              * No relocatable lookup here because the calling executable could
     513             :              * be anywhere
     514             :              */
     515         230 :             ldir = getenv("PGLOCALEDIR");
     516         230 :             if (!ldir)
     517           0 :                 ldir = LOCALEDIR;
     518         230 :             bindtextdomain(PG_TEXTDOMAIN("ecpglib"), ldir);
     519         230 :             already_bound = true;
     520             :         }
     521             : 
     522         230 :         (void) pthread_mutex_unlock(&binddomain_mutex);
     523             : 
     524             : #ifdef WIN32
     525             :         SetLastError(save_errno);
     526             : #else
     527         230 :         errno = save_errno;
     528             : #endif
     529             :     }
     530             : 
     531       13778 :     return dgettext(PG_TEXTDOMAIN("ecpglib"), msgid);
     532             : }
     533             : #endif                          /* ENABLE_NLS */
     534             : 
     535             : struct var_list *ivlist = NULL;
     536             : 
     537             : void
     538          56 : ECPGset_var(int number, void *pointer, int lineno)
     539             : {
     540             :     struct var_list *ptr;
     541             : 
     542          56 :     struct sqlca_t *sqlca = ECPGget_sqlca();
     543             : 
     544          56 :     if (sqlca == NULL)
     545             :     {
     546           0 :         ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
     547             :                    ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
     548           0 :         return;
     549             :     }
     550             : 
     551          56 :     ecpg_init_sqlca(sqlca);
     552             : 
     553         156 :     for (ptr = ivlist; ptr != NULL; ptr = ptr->next)
     554             :     {
     555         100 :         if (ptr->number == number)
     556             :         {
     557             :             /* already known => just change pointer value */
     558           0 :             ptr->pointer = pointer;
     559           0 :             return;
     560             :         }
     561             :     }
     562             : 
     563             :     /* a new one has to be added */
     564          56 :     ptr = (struct var_list *) calloc(1L, sizeof(struct var_list));
     565          56 :     if (!ptr)
     566             :     {
     567           0 :         sqlca = ECPGget_sqlca();
     568             : 
     569           0 :         if (sqlca == NULL)
     570             :         {
     571           0 :             ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
     572             :                        ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
     573           0 :             return;
     574             :         }
     575             : 
     576           0 :         sqlca->sqlcode = ECPG_OUT_OF_MEMORY;
     577           0 :         strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));
     578           0 :         snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "out of memory on line %d", lineno);
     579           0 :         sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
     580             :         /* free all memory we have allocated for the user */
     581           0 :         ECPGfree_auto_mem();
     582             :     }
     583             :     else
     584             :     {
     585          56 :         ptr->number = number;
     586          56 :         ptr->pointer = pointer;
     587          56 :         ptr->next = ivlist;
     588          56 :         ivlist = ptr;
     589             :     }
     590             : }
     591             : 
     592             : void *
     593         204 : ECPGget_var(int number)
     594             : {
     595             :     struct var_list *ptr;
     596             : 
     597         304 :     for (ptr = ivlist; ptr != NULL && ptr->number != number; ptr = ptr->next);
     598         204 :     return (ptr) ? ptr->pointer : NULL;
     599             : }

Generated by: LCOV version 1.14