LCOV - code coverage report
Current view: top level - src/bin/psql - large_obj.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 50 118 42.4 %
Date: 2019-06-18 07:06:57 Functions: 6 8 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * psql - the PostgreSQL interactive terminal
       3             :  *
       4             :  * Copyright (c) 2000-2019, PostgreSQL Global Development Group
       5             :  *
       6             :  * src/bin/psql/large_obj.c
       7             :  */
       8             : #include "postgres_fe.h"
       9             : #include "large_obj.h"
      10             : 
      11             : #include "settings.h"
      12             : #include "common.h"
      13             : 
      14             : #include "common/logging.h"
      15             : 
      16             : static void print_lo_result(const char *fmt,...) pg_attribute_printf(1, 2);
      17             : 
      18             : static void
      19          24 : print_lo_result(const char *fmt,...)
      20             : {
      21             :     va_list     ap;
      22             : 
      23          24 :     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             :     }
      37             : 
      38          24 :     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          24 : }
      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          24 : start_lo_xact(const char *operation, bool *own_transaction)
      57             : {
      58             :     PGTransactionStatusType tstatus;
      59             :     PGresult   *res;
      60             : 
      61          24 :     *own_transaction = false;
      62             : 
      63          24 :     if (!pset.db)
      64             :     {
      65           0 :         pg_log_error("%s: not connected to a database", operation);
      66           0 :         return false;
      67             :     }
      68             : 
      69          24 :     tstatus = PQtransactionStatus(pset.db);
      70             : 
      71          24 :     switch (tstatus)
      72             :     {
      73             :         case PQTRANS_IDLE:
      74             :             /* need to start our own xact */
      75          24 :             if (!(res = PSQLexec("BEGIN")))
      76           0 :                 return false;
      77          24 :             PQclear(res);
      78          24 :             *own_transaction = true;
      79          24 :             break;
      80             :         case PQTRANS_INTRANS:
      81             :             /* use the existing xact */
      82           0 :             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          24 :     return true;
      92             : }
      93             : 
      94             : /*
      95             :  * Clean up after a successful LO operation
      96             :  */
      97             : static bool
      98          24 : finish_lo_xact(const char *operation, bool own_transaction)
      99             : {
     100             :     PGresult   *res;
     101             : 
     102          24 :     if (own_transaction && pset.autocommit)
     103             :     {
     104             :         /* close out our own xact */
     105          24 :         if (!(res = PSQLexec("COMMIT")))
     106             :         {
     107           0 :             res = PSQLexec("ROLLBACK");
     108           0 :             PQclear(res);
     109           0 :             return false;
     110             :         }
     111          24 :         PQclear(res);
     112             :     }
     113             : 
     114          24 :     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();
     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           8 : 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           8 :     if (!start_lo_xact("\\lo_import", &own_transaction))
     184           0 :         return false;
     185             : 
     186           8 :     SetCancelConn();
     187           8 :     loid = lo_import(pset.db, filename_arg);
     188           8 :     ResetCancelConn();
     189             : 
     190           8 :     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           8 :     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           8 :     if (!finish_lo_xact("\\lo_import", own_transaction))
     222           0 :         return false;
     223             : 
     224           8 :     print_lo_result("lo_import %u", loid);
     225             : 
     226           8 :     sprintf(oidbuf, "%u", loid);
     227           8 :     SetVariable(pset.vars, "LASTOID", oidbuf);
     228             : 
     229           8 :     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();
     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             : }
     265             : 
     266             : 
     267             : 
     268             : /*
     269             :  * do_lo_list()
     270             :  *
     271             :  * Show all large objects in database with comments
     272             :  */
     273             : bool
     274           0 : do_lo_list(void)
     275             : {
     276             :     PGresult   *res;
     277             :     char        buf[1024];
     278           0 :     printQueryOpt myopt = pset.popt;
     279             : 
     280           0 :     if (pset.sversion >= 90000)
     281             :     {
     282           0 :         snprintf(buf, sizeof(buf),
     283             :                  "SELECT oid as \"%s\",\n"
     284             :                  "  pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
     285             :                  "  pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
     286             :                  "  FROM pg_catalog.pg_largeobject_metadata "
     287             :                  "  ORDER BY oid",
     288             :                  gettext_noop("ID"),
     289             :                  gettext_noop("Owner"),
     290             :                  gettext_noop("Description"));
     291             :     }
     292             :     else
     293             :     {
     294           0 :         snprintf(buf, sizeof(buf),
     295             :                  "SELECT loid as \"%s\",\n"
     296             :                  "  pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
     297             :                  "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
     298             :                  "ORDER BY 1",
     299             :                  gettext_noop("ID"),
     300             :                  gettext_noop("Description"));
     301             :     }
     302             : 
     303           0 :     res = PSQLexec(buf);
     304           0 :     if (!res)
     305           0 :         return false;
     306             : 
     307           0 :     myopt.topt.tuples_only = false;
     308           0 :     myopt.nullPrint = NULL;
     309           0 :     myopt.title = _("Large objects");
     310           0 :     myopt.translate_header = true;
     311             : 
     312           0 :     printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
     313             : 
     314           0 :     PQclear(res);
     315           0 :     return true;
     316             : }

Generated by: LCOV version 1.13