LCOV - code coverage report
Current view: top level - src/bin/psql - large_obj.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 53.3 % 107 57
Test Date: 2026-03-01 18:15:11 Functions: 85.7 % 7 6
Legend: Lines:     hit not hit

            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           22 : print_lo_result(const char *fmt,...)
      20              : {
      21              :     va_list     ap;
      22              : 
      23           22 :     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           22 :     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           22 : }
      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           22 : start_lo_xact(const char *operation, bool *own_transaction)
      57              : {
      58              :     PGTransactionStatusType tstatus;
      59              :     PGresult   *res;
      60              : 
      61           22 :     *own_transaction = false;
      62              : 
      63           22 :     if (!pset.db)
      64              :     {
      65            0 :         pg_log_error("%s: not connected to a database", operation);
      66            0 :         return false;
      67              :     }
      68              : 
      69           22 :     tstatus = PQtransactionStatus(pset.db);
      70              : 
      71           22 :     switch (tstatus)
      72              :     {
      73           22 :         case PQTRANS_IDLE:
      74              :             /* need to start our own xact */
      75           22 :             if (!(res = PSQLexec("BEGIN")))
      76            0 :                 return false;
      77           22 :             PQclear(res);
      78           22 :             *own_transaction = true;
      79           22 :             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           22 :     return true;
      92              : }
      93              : 
      94              : /*
      95              :  * Clean up after a successful LO operation
      96              :  */
      97              : static bool
      98           22 : finish_lo_xact(const char *operation, bool own_transaction)
      99              : {
     100              :     PGresult   *res;
     101              : 
     102           22 :     if (own_transaction && pset.autocommit)
     103              :     {
     104              :         /* close out our own xact */
     105           22 :         if (!(res = PSQLexec("COMMIT")))
     106              :         {
     107            0 :             res = PSQLexec("ROLLBACK");
     108            0 :             PQclear(res);
     109            0 :             return false;
     110              :         }
     111           22 :         PQclear(res);
     112              :     }
     113              : 
     114           22 :     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            3 : do_lo_export(const char *loid_arg, const char *filename_arg)
     143              : {
     144              :     int         status;
     145              :     bool        own_transaction;
     146              : 
     147            3 :     if (!start_lo_xact("\\lo_export", &own_transaction))
     148            0 :         return false;
     149              : 
     150            3 :     SetCancelConn(NULL);
     151            3 :     status = lo_export(pset.db, atooid(loid_arg), filename_arg);
     152            3 :     ResetCancelConn();
     153              : 
     154              :     /* of course this status is documented nowhere :( */
     155            3 :     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            3 :     if (!finish_lo_xact("\\lo_export", own_transaction))
     162            0 :         return false;
     163              : 
     164            3 :     print_lo_result("lo_export");
     165              : 
     166            3 :     return true;
     167              : }
     168              : 
     169              : 
     170              : /*
     171              :  * do_lo_import()
     172              :  *
     173              :  * Copy large object from file to database
     174              :  */
     175              : bool
     176            7 : 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            7 :     if (!start_lo_xact("\\lo_import", &own_transaction))
     184            0 :         return false;
     185              : 
     186            7 :     SetCancelConn(NULL);
     187            7 :     loid = lo_import(pset.db, filename_arg);
     188            7 :     ResetCancelConn();
     189              : 
     190            7 :     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            7 :     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 :             free(cmdbuf);
     214            0 :             return fail_lo_xact("\\lo_import", own_transaction);
     215              :         }
     216              : 
     217            0 :         PQclear(res);
     218            0 :         free(cmdbuf);
     219              :     }
     220              : 
     221            7 :     if (!finish_lo_xact("\\lo_import", own_transaction))
     222            0 :         return false;
     223              : 
     224            7 :     print_lo_result("lo_import %u", loid);
     225              : 
     226            7 :     sprintf(oidbuf, "%u", loid);
     227            7 :     SetVariable(pset.vars, "LASTOID", oidbuf);
     228              : 
     229            7 :     return true;
     230              : }
     231              : 
     232              : 
     233              : /*
     234              :  * do_lo_unlink()
     235              :  *
     236              :  * removes a large object out of the database
     237              :  */
     238              : bool
     239           12 : do_lo_unlink(const char *loid_arg)
     240              : {
     241              :     int         status;
     242           12 :     Oid         loid = atooid(loid_arg);
     243              :     bool        own_transaction;
     244              : 
     245           12 :     if (!start_lo_xact("\\lo_unlink", &own_transaction))
     246            0 :         return false;
     247              : 
     248           12 :     SetCancelConn(NULL);
     249           12 :     status = lo_unlink(pset.db, loid);
     250           12 :     ResetCancelConn();
     251              : 
     252           12 :     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           12 :     if (!finish_lo_xact("\\lo_unlink", own_transaction))
     259            0 :         return false;
     260              : 
     261           12 :     print_lo_result("lo_unlink %u", loid);
     262              : 
     263           12 :     return true;
     264              : }
        

Generated by: LCOV version 2.0-1