LCOV - differential code coverage report
Current view: top level - src/bin/psql - large_obj.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 53.3 % 107 57 50 57
Current Date: 2023-04-08 15:15:32 Functions: 85.7 % 7 6 1 6
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*
       2                 :  * psql - the PostgreSQL interactive terminal
       3                 :  *
       4                 :  * Copyright (c) 2000-2023, 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 CBC          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 UBC           0 :             fputs("<p>", pset.queryFout);
      27                 : 
      28 CBC           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 UBC           0 :             fputs("</p>\n", pset.queryFout);
      34                 :         else
      35 CBC           1 :             fputs("\n", pset.queryFout);
      36                 :     }
      37                 : 
      38              22 :     if (pset.logfile)
      39                 :     {
      40 UBC           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 CBC          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 UBC           0 :         pg_log_error("%s: not connected to a database", operation);
      66               0 :         return false;
      67                 :     }
      68                 : 
      69 CBC          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 UBC           0 :                 return false;
      77 CBC          22 :             PQclear(res);
      78              22 :             *own_transaction = true;
      79              22 :             break;
      80 UBC           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 CBC          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 UBC           0 :             res = PSQLexec("ROLLBACK");
     108               0 :             PQclear(res);
     109               0 :             return false;
     110                 :         }
     111 CBC          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 UBC           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 CBC           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 UBC           0 :         return false;
     149                 : 
     150 CBC           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 UBC           0 :         pg_log_info("%s", PQerrorMessage(pset.db));
     158               0 :         return fail_lo_xact("\\lo_export", own_transaction);
     159                 :     }
     160                 : 
     161 CBC           3 :     if (!finish_lo_xact("\\lo_export", own_transaction))
     162 UBC           0 :         return false;
     163                 : 
     164 CBC           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 UBC           0 :         return false;
     185                 : 
     186 CBC           7 :     SetCancelConn(NULL);
     187               7 :     loid = lo_import(pset.db, filename_arg);
     188               7 :     ResetCancelConn();
     189                 : 
     190               7 :     if (loid == InvalidOid)
     191                 :     {
     192 UBC           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 CBC           7 :     if (comment_arg)
     198                 :     {
     199                 :         char       *cmdbuf;
     200                 :         char       *bufptr;
     201 UBC           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 CBC           7 :     if (!finish_lo_xact("\\lo_import", own_transaction))
     222 UBC           0 :         return false;
     223                 : 
     224 CBC           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 UBC           0 :         return false;
     247                 : 
     248 CBC          12 :     SetCancelConn(NULL);
     249              12 :     status = lo_unlink(pset.db, loid);
     250              12 :     ResetCancelConn();
     251                 : 
     252              12 :     if (status == -1)
     253                 :     {
     254 UBC           0 :         pg_log_info("%s", PQerrorMessage(pset.db));
     255               0 :         return fail_lo_xact("\\lo_unlink", own_transaction);
     256                 :     }
     257                 : 
     258 CBC          12 :     if (!finish_lo_xact("\\lo_unlink", own_transaction))
     259 UBC           0 :         return false;
     260                 : 
     261 CBC          12 :     print_lo_result("lo_unlink %u", loid);
     262                 : 
     263              12 :     return true;
     264                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a