LCOV - code coverage report
Current view: top level - src/bin/psql - large_obj.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 53.1 % 128 68
Test Date: 2026-01-26 10:56:24 Functions: 85.7 % 7 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 34.6 % 52 18

             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                 :           7 : print_lo_result(const char *fmt,...)
      20                 :             : {
      21                 :           7 :         va_list         ap;
      22                 :             : 
      23         [ +  - ]:           7 :         if (!pset.quiet)
      24                 :             :         {
      25         [ #  # ]:           0 :                 if (pset.popt.topt.format == PRINT_HTML)
      26                 :           0 :                         fputs("<p>", pset.queryFout);
      27                 :             : 
      28                 :           0 :                 va_start(ap, fmt);
      29                 :           0 :                 vfprintf(pset.queryFout, fmt, ap);
      30                 :           0 :                 va_end(ap);
      31                 :             : 
      32         [ #  # ]:           0 :                 if (pset.popt.topt.format == PRINT_HTML)
      33                 :           0 :                         fputs("</p>\n", pset.queryFout);
      34                 :             :                 else
      35                 :           0 :                         fputs("\n", pset.queryFout);
      36                 :           0 :         }
      37                 :             : 
      38         [ +  - ]:           7 :         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                 :           0 :         }
      45                 :           7 : }
      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                 :           7 : start_lo_xact(const char *operation, bool *own_transaction)
      57                 :             : {
      58                 :           7 :         PGTransactionStatusType tstatus;
      59                 :           7 :         PGresult   *res;
      60                 :             : 
      61                 :           7 :         *own_transaction = false;
      62                 :             : 
      63         [ +  - ]:           7 :         if (!pset.db)
      64                 :             :         {
      65                 :           0 :                 pg_log_error("%s: not connected to a database", operation);
      66                 :           0 :                 return false;
      67                 :             :         }
      68                 :             : 
      69                 :           7 :         tstatus = PQtransactionStatus(pset.db);
      70                 :             : 
      71   [ -  -  +  - ]:           7 :         switch (tstatus)
      72                 :             :         {
      73                 :             :                 case PQTRANS_IDLE:
      74                 :             :                         /* need to start our own xact */
      75         [ +  - ]:           7 :                         if (!(res = PSQLexec("BEGIN")))
      76                 :           0 :                                 return false;
      77                 :           7 :                         PQclear(res);
      78                 :           7 :                         *own_transaction = true;
      79                 :           7 :                         break;
      80                 :             :                 case PQTRANS_INTRANS:
      81                 :             :                         /* use the existing xact */
      82                 :             :                         break;
      83                 :             :                 case PQTRANS_INERROR:
      84                 :           0 :                         pg_log_error("%s: current transaction is aborted", operation);
      85                 :           0 :                         return false;
      86                 :             :                 default:
      87                 :           0 :                         pg_log_error("%s: unknown transaction status", operation);
      88                 :           0 :                         return false;
      89                 :             :         }
      90                 :             : 
      91                 :           7 :         return true;
      92                 :           7 : }
      93                 :             : 
      94                 :             : /*
      95                 :             :  * Clean up after a successful LO operation
      96                 :             :  */
      97                 :             : static bool
      98                 :           7 : finish_lo_xact(const char *operation, bool own_transaction)
      99                 :             : {
     100                 :           7 :         PGresult   *res;
     101                 :             : 
     102   [ +  -  -  + ]:           7 :         if (own_transaction && pset.autocommit)
     103                 :             :         {
     104                 :             :                 /* close out our own xact */
     105         [ +  - ]:           7 :                 if (!(res = PSQLexec("COMMIT")))
     106                 :             :                 {
     107                 :           0 :                         res = PSQLexec("ROLLBACK");
     108                 :           0 :                         PQclear(res);
     109                 :           0 :                         return false;
     110                 :             :                 }
     111                 :           7 :                 PQclear(res);
     112                 :           7 :         }
     113                 :             : 
     114                 :           7 :         return true;
     115                 :           7 : }
     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                 :           0 :         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                 :           0 :         }
     131                 :             : 
     132                 :           0 :         return false;                           /* always */
     133                 :           0 : }
     134                 :             : 
     135                 :             : 
     136                 :             : /*
     137                 :             :  * do_lo_export()
     138                 :             :  *
     139                 :             :  * Write a large object to a file
     140                 :             :  */
     141                 :             : bool
     142                 :           1 : do_lo_export(const char *loid_arg, const char *filename_arg)
     143                 :             : {
     144                 :           1 :         int                     status;
     145                 :           1 :         bool            own_transaction;
     146                 :             : 
     147         [ -  + ]:           1 :         if (!start_lo_xact("\\lo_export", &own_transaction))
     148                 :           0 :                 return false;
     149                 :             : 
     150                 :           1 :         SetCancelConn(NULL);
     151                 :           1 :         status = lo_export(pset.db, atooid(loid_arg), filename_arg);
     152                 :           1 :         ResetCancelConn();
     153                 :             : 
     154                 :             :         /* of course this status is documented nowhere :( */
     155         [ -  + ]:           1 :         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         [ +  - ]:           1 :         if (!finish_lo_xact("\\lo_export", own_transaction))
     162                 :           0 :                 return false;
     163                 :             : 
     164                 :           1 :         print_lo_result("lo_export");
     165                 :             : 
     166                 :           1 :         return true;
     167                 :           1 : }
     168                 :             : 
     169                 :             : 
     170                 :             : /*
     171                 :             :  * do_lo_import()
     172                 :             :  *
     173                 :             :  * Copy large object from file to database
     174                 :             :  */
     175                 :             : bool
     176                 :           2 : do_lo_import(const char *filename_arg, const char *comment_arg)
     177                 :             : {
     178                 :           2 :         PGresult   *res;
     179                 :           2 :         Oid                     loid;
     180                 :           2 :         char            oidbuf[32];
     181                 :           2 :         bool            own_transaction;
     182                 :             : 
     183         [ +  - ]:           2 :         if (!start_lo_xact("\\lo_import", &own_transaction))
     184                 :           0 :                 return false;
     185                 :             : 
     186                 :           2 :         SetCancelConn(NULL);
     187                 :           2 :         loid = lo_import(pset.db, filename_arg);
     188                 :           2 :         ResetCancelConn();
     189                 :             : 
     190         [ +  - ]:           2 :         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         [ +  - ]:           2 :         if (comment_arg)
     198                 :             :         {
     199                 :           0 :                 char       *cmdbuf;
     200                 :           0 :                 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 :                         free(cmdbuf);
     214                 :           0 :                         return fail_lo_xact("\\lo_import", own_transaction);
     215                 :             :                 }
     216                 :             : 
     217                 :           0 :                 PQclear(res);
     218                 :           0 :                 free(cmdbuf);
     219         [ #  # ]:           0 :         }
     220                 :             : 
     221         [ +  - ]:           2 :         if (!finish_lo_xact("\\lo_import", own_transaction))
     222                 :           0 :                 return false;
     223                 :             : 
     224                 :           2 :         print_lo_result("lo_import %u", loid);
     225                 :             : 
     226                 :           2 :         sprintf(oidbuf, "%u", loid);
     227                 :           2 :         SetVariable(pset.vars, "LASTOID", oidbuf);
     228                 :             : 
     229                 :           2 :         return true;
     230                 :           2 : }
     231                 :             : 
     232                 :             : 
     233                 :             : /*
     234                 :             :  * do_lo_unlink()
     235                 :             :  *
     236                 :             :  * removes a large object out of the database
     237                 :             :  */
     238                 :             : bool
     239                 :           4 : do_lo_unlink(const char *loid_arg)
     240                 :             : {
     241                 :           4 :         int                     status;
     242                 :           4 :         Oid                     loid = atooid(loid_arg);
     243                 :           4 :         bool            own_transaction;
     244                 :             : 
     245         [ -  + ]:           4 :         if (!start_lo_xact("\\lo_unlink", &own_transaction))
     246                 :           0 :                 return false;
     247                 :             : 
     248                 :           4 :         SetCancelConn(NULL);
     249                 :           4 :         status = lo_unlink(pset.db, loid);
     250                 :           4 :         ResetCancelConn();
     251                 :             : 
     252         [ +  - ]:           4 :         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         [ +  - ]:           4 :         if (!finish_lo_xact("\\lo_unlink", own_transaction))
     259                 :           0 :                 return false;
     260                 :             : 
     261                 :           4 :         print_lo_result("lo_unlink %u", loid);
     262                 :             : 
     263                 :           4 :         return true;
     264                 :           4 : }
        

Generated by: LCOV version 2.3.2-1