LCOV - code coverage report
Current view: top level - src/bin/psql - large_obj.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 53.3 % 107 57
Test Date: 2026-07-23 06:15:50 Functions: 85.7 % 7 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 42.0 % 50 21

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * psql - the PostgreSQL interactive terminal
       3                 :             :  *
       4                 :             :  * Copyright (c) 2000-2026, PostgreSQL Global Development Group
       5                 :             :  *
       6                 :             :  * src/bin/psql/large_obj.c
       7                 :             :  */
       8                 :             : #include "postgres_fe.h"
       9                 :             : 
      10                 :             : #include "common.h"
      11                 :             : #include "common/logging.h"
      12                 :             : #include "fe_utils/cancel.h"
      13                 :             : #include "large_obj.h"
      14                 :             : #include "settings.h"
      15                 :             : 
      16                 :             : static void print_lo_result(const char *fmt, ...) pg_attribute_printf(1, 2);
      17                 :             : 
      18                 :             : static void
      19                 :          29 : print_lo_result(const char *fmt, ...)
      20                 :             : {
      21                 :             :     va_list     ap;
      22                 :             : 
      23         [ +  + ]:          29 :     if (!pset.quiet)
      24                 :             :     {
      25         [ -  + ]:           1 :         if (pset.popt.topt.format == PRINT_HTML)
      26                 :           0 :             fputs("<p>", pset.queryFout);
      27                 :             : 
      28                 :           1 :         va_start(ap, fmt);
      29                 :           1 :         vfprintf(pset.queryFout, fmt, ap);
      30                 :           1 :         va_end(ap);
      31                 :             : 
      32         [ -  + ]:           1 :         if (pset.popt.topt.format == PRINT_HTML)
      33                 :           0 :             fputs("</p>\n", pset.queryFout);
      34                 :             :         else
      35                 :           1 :             fputs("\n", pset.queryFout);
      36                 :             :     }
      37                 :             : 
      38         [ -  + ]:          29 :     if (pset.logfile)
      39                 :             :     {
      40                 :           0 :         va_start(ap, fmt);
      41                 :           0 :         vfprintf(pset.logfile, fmt, ap);
      42                 :           0 :         va_end(ap);
      43                 :           0 :         fputs("\n", pset.logfile);
      44                 :             :     }
      45                 :          29 : }
      46                 :             : 
      47                 :             : 
      48                 :             : /*
      49                 :             :  * Prepare to do a large-object operation.  We *must* be inside a transaction
      50                 :             :  * block for all these operations, so start one if needed.
      51                 :             :  *
      52                 :             :  * Returns true if okay, false if failed.  *own_transaction is set to indicate
      53                 :             :  * if we started our own transaction or not.
      54                 :             :  */
      55                 :             : static bool
      56                 :          29 : start_lo_xact(const char *operation, bool *own_transaction)
      57                 :             : {
      58                 :             :     PGTransactionStatusType tstatus;
      59                 :             :     PGresult   *res;
      60                 :             : 
      61                 :          29 :     *own_transaction = false;
      62                 :             : 
      63         [ -  + ]:          29 :     if (!pset.db)
      64                 :             :     {
      65                 :           0 :         pg_log_error("%s: not connected to a database", operation);
      66                 :           0 :         return false;
      67                 :             :     }
      68                 :             : 
      69                 :          29 :     tstatus = PQtransactionStatus(pset.db);
      70                 :             : 
      71   [ +  -  -  - ]:          29 :     switch (tstatus)
      72                 :             :     {
      73                 :          29 :         case PQTRANS_IDLE:
      74                 :             :             /* need to start our own xact */
      75         [ -  + ]:          29 :             if (!(res = PSQLexec("BEGIN")))
      76                 :           0 :                 return false;
      77                 :          29 :             PQclear(res);
      78                 :          29 :             *own_transaction = true;
      79                 :          29 :             break;
      80                 :           0 :         case PQTRANS_INTRANS:
      81                 :             :             /* use the existing xact */
      82                 :           0 :             break;
      83                 :           0 :         case PQTRANS_INERROR:
      84                 :           0 :             pg_log_error("%s: current transaction is aborted", operation);
      85                 :           0 :             return false;
      86                 :           0 :         default:
      87                 :           0 :             pg_log_error("%s: unknown transaction status", operation);
      88                 :           0 :             return false;
      89                 :             :     }
      90                 :             : 
      91                 :          29 :     return true;
      92                 :             : }
      93                 :             : 
      94                 :             : /*
      95                 :             :  * Clean up after a successful LO operation
      96                 :             :  */
      97                 :             : static bool
      98                 :          29 : finish_lo_xact(const char *operation, bool own_transaction)
      99                 :             : {
     100                 :             :     PGresult   *res;
     101                 :             : 
     102   [ +  -  +  - ]:          29 :     if (own_transaction && pset.autocommit)
     103                 :             :     {
     104                 :             :         /* close out our own xact */
     105         [ -  + ]:          29 :         if (!(res = PSQLexec("COMMIT")))
     106                 :             :         {
     107                 :           0 :             res = PSQLexec("ROLLBACK");
     108                 :           0 :             PQclear(res);
     109                 :           0 :             return false;
     110                 :             :         }
     111                 :          29 :         PQclear(res);
     112                 :             :     }
     113                 :             : 
     114                 :          29 :     return true;
     115                 :             : }
     116                 :             : 
     117                 :             : /*
     118                 :             :  * Clean up after a failed LO operation
     119                 :             :  */
     120                 :             : static bool
     121                 :           0 : fail_lo_xact(const char *operation, bool own_transaction)
     122                 :             : {
     123                 :             :     PGresult   *res;
     124                 :             : 
     125   [ #  #  #  # ]:           0 :     if (own_transaction && pset.autocommit)
     126                 :             :     {
     127                 :             :         /* close out our own xact */
     128                 :           0 :         res = PSQLexec("ROLLBACK");
     129                 :           0 :         PQclear(res);
     130                 :             :     }
     131                 :             : 
     132                 :           0 :     return false;               /* always */
     133                 :             : }
     134                 :             : 
     135                 :             : 
     136                 :             : /*
     137                 :             :  * do_lo_export()
     138                 :             :  *
     139                 :             :  * Write a large object to a file
     140                 :             :  */
     141                 :             : bool
     142                 :           4 : do_lo_export(const char *loid_arg, const char *filename_arg)
     143                 :             : {
     144                 :             :     int         status;
     145                 :             :     bool        own_transaction;
     146                 :             : 
     147         [ -  + ]:           4 :     if (!start_lo_xact("\\lo_export", &own_transaction))
     148                 :           0 :         return false;
     149                 :             : 
     150                 :           4 :     SetCancelConn(NULL);
     151                 :           4 :     status = lo_export(pset.db, atooid(loid_arg), filename_arg);
     152                 :           4 :     ResetCancelConn();
     153                 :             : 
     154                 :             :     /* of course this status is documented nowhere :( */
     155         [ -  + ]:           4 :     if (status != 1)
     156                 :             :     {
     157                 :           0 :         pg_log_info("%s", PQerrorMessage(pset.db));
     158                 :           0 :         return fail_lo_xact("\\lo_export", own_transaction);
     159                 :             :     }
     160                 :             : 
     161         [ -  + ]:           4 :     if (!finish_lo_xact("\\lo_export", own_transaction))
     162                 :           0 :         return false;
     163                 :             : 
     164                 :           4 :     print_lo_result("lo_export");
     165                 :             : 
     166                 :           4 :     return true;
     167                 :             : }
     168                 :             : 
     169                 :             : 
     170                 :             : /*
     171                 :             :  * do_lo_import()
     172                 :             :  *
     173                 :             :  * Copy large object from file to database
     174                 :             :  */
     175                 :             : bool
     176                 :           9 : do_lo_import(const char *filename_arg, const char *comment_arg)
     177                 :             : {
     178                 :             :     PGresult   *res;
     179                 :             :     Oid         loid;
     180                 :             :     char        oidbuf[32];
     181                 :             :     bool        own_transaction;
     182                 :             : 
     183         [ -  + ]:           9 :     if (!start_lo_xact("\\lo_import", &own_transaction))
     184                 :           0 :         return false;
     185                 :             : 
     186                 :           9 :     SetCancelConn(NULL);
     187                 :           9 :     loid = lo_import(pset.db, filename_arg);
     188                 :           9 :     ResetCancelConn();
     189                 :             : 
     190         [ -  + ]:           9 :     if (loid == InvalidOid)
     191                 :             :     {
     192                 :           0 :         pg_log_info("%s", PQerrorMessage(pset.db));
     193                 :           0 :         return fail_lo_xact("\\lo_import", own_transaction);
     194                 :             :     }
     195                 :             : 
     196                 :             :     /* insert description if given */
     197         [ -  + ]:           9 :     if (comment_arg)
     198                 :             :     {
     199                 :             :         char       *cmdbuf;
     200                 :             :         char       *bufptr;
     201                 :           0 :         size_t      slen = strlen(comment_arg);
     202                 :             : 
     203                 :           0 :         cmdbuf = pg_malloc_extended(slen * 2 + 256, MCXT_ALLOC_NO_OOM);
     204         [ #  # ]:           0 :         if (!cmdbuf)
     205                 :           0 :             return fail_lo_xact("\\lo_import", own_transaction);
     206                 :           0 :         sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
     207                 :           0 :         bufptr = cmdbuf + strlen(cmdbuf);
     208                 :           0 :         bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
     209                 :           0 :         strcpy(bufptr, "'");
     210                 :             : 
     211         [ #  # ]:           0 :         if (!(res = PSQLexec(cmdbuf)))
     212                 :             :         {
     213                 :           0 :             pg_free(cmdbuf);
     214                 :           0 :             return fail_lo_xact("\\lo_import", own_transaction);
     215                 :             :         }
     216                 :             : 
     217                 :           0 :         PQclear(res);
     218                 :           0 :         pg_free(cmdbuf);
     219                 :             :     }
     220                 :             : 
     221         [ -  + ]:           9 :     if (!finish_lo_xact("\\lo_import", own_transaction))
     222                 :           0 :         return false;
     223                 :             : 
     224                 :           9 :     print_lo_result("lo_import %u", loid);
     225                 :             : 
     226                 :           9 :     sprintf(oidbuf, "%u", loid);
     227                 :           9 :     SetVariable(pset.vars, "LASTOID", oidbuf);
     228                 :             : 
     229                 :           9 :     return true;
     230                 :             : }
     231                 :             : 
     232                 :             : 
     233                 :             : /*
     234                 :             :  * do_lo_unlink()
     235                 :             :  *
     236                 :             :  * removes a large object out of the database
     237                 :             :  */
     238                 :             : bool
     239                 :          16 : do_lo_unlink(const char *loid_arg)
     240                 :             : {
     241                 :             :     int         status;
     242                 :          16 :     Oid         loid = atooid(loid_arg);
     243                 :             :     bool        own_transaction;
     244                 :             : 
     245         [ -  + ]:          16 :     if (!start_lo_xact("\\lo_unlink", &own_transaction))
     246                 :           0 :         return false;
     247                 :             : 
     248                 :          16 :     SetCancelConn(NULL);
     249                 :          16 :     status = lo_unlink(pset.db, loid);
     250                 :          16 :     ResetCancelConn();
     251                 :             : 
     252         [ -  + ]:          16 :     if (status == -1)
     253                 :             :     {
     254                 :           0 :         pg_log_info("%s", PQerrorMessage(pset.db));
     255                 :           0 :         return fail_lo_xact("\\lo_unlink", own_transaction);
     256                 :             :     }
     257                 :             : 
     258         [ -  + ]:          16 :     if (!finish_lo_xact("\\lo_unlink", own_transaction))
     259                 :           0 :         return false;
     260                 :             : 
     261                 :          16 :     print_lo_result("lo_unlink %u", loid);
     262                 :             : 
     263                 :          16 :     return true;
     264                 :             : }
        

Generated by: LCOV version 2.0-1