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

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

Generated by: LCOV version 1.14