LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/ecpglib - misc.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 70.5 % 241 170
Test Date: 2026-03-17 17:14:49 Functions: 87.5 % 16 14
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      3208552 : ecpg_init_sqlca(struct sqlca_t *sqlca)
      68              : {
      69      3208552 :     memcpy(sqlca, &sqlca_init, sizeof(struct sqlca_t));
      70      3208552 : }
      71              : 
      72              : bool
      73         7746 : ecpg_init(const struct connection *con, const char *connection_name, const int lineno)
      74              : {
      75         7746 :     struct sqlca_t *sqlca = ECPGget_sqlca();
      76              : 
      77         7746 :     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         7746 :     ecpg_init_sqlca(sqlca);
      85         7746 :     if (con == NULL)
      86              :     {
      87           43 :         ecpg_raise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
      88           12 :                    connection_name ? connection_name : ecpg_gettext("NULL"));
      89           31 :         return false;
      90              :     }
      91              : 
      92         7715 :     return true;
      93              : }
      94              : 
      95              : static void
      96          136 : ecpg_sqlca_key_destructor(void *arg)
      97              : {
      98          136 :     free(arg);                  /* sqlca structure allocated in ECPGget_sqlca */
      99          136 : }
     100              : 
     101              : static void
     102          129 : ecpg_sqlca_key_init(void)
     103              : {
     104          129 :     pthread_key_create(&sqlca_key, ecpg_sqlca_key_destructor);
     105          129 : }
     106              : 
     107              : struct sqlca_t *
     108      6436510 : ECPGget_sqlca(void)
     109              : {
     110              :     struct sqlca_t *sqlca;
     111              : 
     112      6436510 :     pthread_once(&sqlca_key_once, ecpg_sqlca_key_init);
     113              : 
     114      6436510 :     sqlca = pthread_getspecific(sqlca_key);
     115      6436510 :     if (sqlca == NULL)
     116              :     {
     117          261 :         sqlca = malloc(sizeof(struct sqlca_t));
     118          261 :         if (sqlca == NULL)
     119            0 :             return NULL;
     120          261 :         ecpg_init_sqlca(sqlca);
     121          261 :         pthread_setspecific(sqlca_key, sqlca);
     122              :     }
     123      6436510 :     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          250 : ECPGtrans(int lineno, const char *connection_name, const char *transaction)
     161              : {
     162              :     PGresult   *res;
     163          250 :     struct connection *con = ecpg_get_connection(connection_name);
     164              : 
     165          250 :     if (!ecpg_init(con, connection_name, lineno))
     166            0 :         return false;
     167              : 
     168          250 :     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          250 :     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          250 :         if (PQtransactionStatus(con->connection) == PQTRANS_IDLE &&
     181           54 :             !con->autocommit &&
     182           48 :             strncmp(transaction, "begin", 5) != 0 &&
     183            2 :             strncmp(transaction, "start", 5) != 0 &&
     184            2 :             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          250 :         res = PQexec(con->connection, transaction);
     194          250 :         if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
     195            0 :             return false;
     196          250 :         PQclear(res);
     197              :     }
     198              : 
     199          250 :     return true;
     200              : }
     201              : 
     202              : 
     203              : void
     204          119 : ECPGdebug(int n, FILE *dbgs)
     205              : {
     206              :     /* Interlock against concurrent executions of ECPGdebug() */
     207          119 :     pthread_mutex_lock(&debug_init_mutex);
     208              : 
     209              :     /* Prevent ecpg_log() from printing while we change settings */
     210          119 :     pthread_mutex_lock(&debug_mutex);
     211              : 
     212          119 :     if (n > 100)
     213              :     {
     214          119 :         ecpg_internal_regression_mode = true;
     215          119 :         simple_debug = n - 100;
     216              :     }
     217              :     else
     218            0 :         simple_debug = n;
     219              : 
     220          119 :     debugstream = dbgs;
     221              : 
     222              :     /* We must release debug_mutex before invoking ecpg_log() ... */
     223          119 :     pthread_mutex_unlock(&debug_mutex);
     224              : 
     225              :     /* ... but keep holding debug_init_mutex to avoid racy printout */
     226          119 :     ecpg_log("ECPGdebug: set to %d\n", simple_debug);
     227              : 
     228          119 :     pthread_mutex_unlock(&debug_init_mutex);
     229          119 : }
     230              : 
     231              : void
     232        29442 : 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        29442 :     if (!simple_debug)
     246        22542 :         return;
     247              : 
     248              :     /* localize the error message string */
     249         6900 :     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         6900 :     bufsize = strlen(intl_format) + 100;
     256         6900 :     fmt = (char *) malloc(bufsize);
     257         6900 :     if (fmt == NULL)
     258            0 :         return;
     259              : 
     260         6900 :     if (ecpg_internal_regression_mode)
     261         6900 :         snprintf(fmt, bufsize, "[NO_PID]: %s", intl_format);
     262              :     else
     263            0 :         snprintf(fmt, bufsize, "[%d]: %s", (int) getpid(), intl_format);
     264              : 
     265         6900 :     sqlca = ECPGget_sqlca();
     266              : 
     267         6900 :     pthread_mutex_lock(&debug_mutex);
     268              : 
     269              :     /* Now that we hold the mutex, recheck simple_debug */
     270         6900 :     if (simple_debug)
     271              :     {
     272         6900 :         va_start(ap, format);
     273         6900 :         vfprintf(debugstream, fmt, ap);
     274         6900 :         va_end(ap);
     275              : 
     276              :         /* dump out internal sqlca variables */
     277         6900 :         if (ecpg_internal_regression_mode && sqlca != NULL)
     278              :         {
     279         6900 :             fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
     280         6900 :                     sqlca->sqlcode, sqlca->sqlstate);
     281              :         }
     282              : 
     283         6900 :         fflush(debugstream);
     284              :     }
     285              : 
     286         6900 :     pthread_mutex_unlock(&debug_mutex);
     287              : 
     288         6900 :     free(fmt);
     289              : }
     290              : 
     291              : void
     292         5370 : ECPGset_noind_null(enum ECPGttype type, void *ptr)
     293              : {
     294         5370 :     switch (type)
     295              :     {
     296         1890 :         case ECPGt_char:
     297              :         case ECPGt_unsigned_char:
     298              :         case ECPGt_string:
     299         1890 :             *((char *) ptr) = '\0';
     300         1890 :             break;
     301            4 :         case ECPGt_short:
     302              :         case ECPGt_unsigned_short:
     303            4 :             *((short int *) ptr) = SHRT_MIN;
     304            4 :             break;
     305            4 :         case ECPGt_int:
     306              :         case ECPGt_unsigned_int:
     307            4 :             *((int *) ptr) = INT_MIN;
     308            4 :             break;
     309           10 :         case ECPGt_long:
     310              :         case ECPGt_unsigned_long:
     311              :         case ECPGt_date:
     312           10 :             *((long *) ptr) = LONG_MIN;
     313           10 :             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            4 :         case ECPGt_float:
     319            4 :             memset(ptr, 0xff, sizeof(float));
     320            4 :             break;
     321            8 :         case ECPGt_double:
     322            8 :             memset(ptr, 0xff, sizeof(double));
     323            8 :             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         3438 :         case ECPGt_decimal:
     332         3438 :             memset(ptr, 0, sizeof(decimal));
     333         3438 :             ((decimal *) ptr)->sign = NUMERIC_NULL;
     334         3438 :             break;
     335            4 :         case ECPGt_numeric:
     336            4 :             memset(ptr, 0, sizeof(numeric));
     337            4 :             ((numeric *) ptr)->sign = NUMERIC_NULL;
     338            4 :             break;
     339            0 :         case ECPGt_interval:
     340            0 :             memset(ptr, 0xff, sizeof(interval));
     341            0 :             break;
     342            6 :         case ECPGt_timestamp:
     343            6 :             memset(ptr, 0xff, sizeof(timestamp));
     344            6 :             break;
     345            2 :         default:
     346            2 :             break;
     347              :     }
     348         5370 : }
     349              : 
     350              : static bool
     351           22 : _check(const unsigned char *ptr, int length)
     352              : {
     353          118 :     for (length--; length >= 0; length--)
     354          104 :         if (ptr[length] != 0xff)
     355            8 :             return false;
     356              : 
     357           14 :     return true;
     358              : }
     359              : 
     360              : bool
     361         7326 : ECPGis_noind_null(enum ECPGttype type, const void *ptr)
     362              : {
     363         7326 :     switch (type)
     364              :     {
     365           40 :         case ECPGt_char:
     366              :         case ECPGt_unsigned_char:
     367              :         case ECPGt_string:
     368           40 :             if (*((const char *) ptr) == '\0')
     369            4 :                 return true;
     370           36 :             break;
     371            8 :         case ECPGt_short:
     372              :         case ECPGt_unsigned_short:
     373            8 :             if (*((const short int *) ptr) == SHRT_MIN)
     374            4 :                 return true;
     375            4 :             break;
     376         1836 :         case ECPGt_int:
     377              :         case ECPGt_unsigned_int:
     378         1836 :             if (*((const int *) ptr) == INT_MIN)
     379            4 :                 return true;
     380         1832 :             break;
     381           36 :         case ECPGt_long:
     382              :         case ECPGt_unsigned_long:
     383              :         case ECPGt_date:
     384           36 :             if (*((const long *) ptr) == LONG_MIN)
     385           10 :                 return true;
     386           26 :             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            8 :         case ECPGt_float:
     393            8 :             return _check(ptr, sizeof(float));
     394              :             break;
     395            8 :         case ECPGt_double:
     396            8 :             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         5376 :         case ECPGt_decimal:
     407         5376 :             if (((const decimal *) ptr)->sign == NUMERIC_NULL)
     408          438 :                 return true;
     409         4938 :             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            6 :         case ECPGt_timestamp:
     418            6 :             return _check(ptr, sizeof(timestamp));
     419              :             break;
     420            8 :         default:
     421            8 :             break;
     422              :     }
     423              : 
     424         6844 :     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         7045 : 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         7045 :     if (!already_bound)
     497              :     {
     498              :         /* dgettext() preserves errno, but bindtextdomain() doesn't */
     499              : #ifdef WIN32
     500              :         int         save_errno = GetLastError();
     501              : #else
     502          117 :         int         save_errno = errno;
     503              : #endif
     504              : 
     505          117 :         (void) pthread_mutex_lock(&binddomain_mutex);
     506              : 
     507          117 :         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          117 :             ldir = getenv("PGLOCALEDIR");
     516          117 :             if (!ldir)
     517            0 :                 ldir = LOCALEDIR;
     518          117 :             bindtextdomain(PG_TEXTDOMAIN("ecpglib"), ldir);
     519          117 :             already_bound = true;
     520              :         }
     521              : 
     522          117 :         (void) pthread_mutex_unlock(&binddomain_mutex);
     523              : 
     524              : #ifdef WIN32
     525              :         SetLastError(save_errno);
     526              : #else
     527          117 :         errno = save_errno;
     528              : #endif
     529              :     }
     530              : 
     531         7045 :     return dgettext(PG_TEXTDOMAIN("ecpglib"), msgid);
     532              : }
     533              : #endif                          /* ENABLE_NLS */
     534              : 
     535              : struct var_list *ivlist = NULL;
     536              : 
     537              : void
     538           28 : ECPGset_var(int number, void *pointer, int lineno)
     539              : {
     540              :     struct var_list *ptr;
     541              : 
     542           28 :     struct sqlca_t *sqlca = ECPGget_sqlca();
     543              : 
     544           28 :     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           28 :     ecpg_init_sqlca(sqlca);
     552              : 
     553           78 :     for (ptr = ivlist; ptr != NULL; ptr = ptr->next)
     554              :     {
     555           50 :         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           28 :     ptr = (struct var_list *) calloc(1L, sizeof(struct var_list));
     565           28 :     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           28 :         ptr->number = number;
     586           28 :         ptr->pointer = pointer;
     587           28 :         ptr->next = ivlist;
     588           28 :         ivlist = ptr;
     589              :     }
     590              : }
     591              : 
     592              : void *
     593          102 : ECPGget_var(int number)
     594              : {
     595              :     struct var_list *ptr;
     596              : 
     597          152 :     for (ptr = ivlist; ptr != NULL && ptr->number != number; ptr = ptr->next);
     598          102 :     return (ptr) ? ptr->pointer : NULL;
     599              : }
        

Generated by: LCOV version 2.0-1