mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-30 18:11:18 +02:00
fc9a62af3f
The original placement of this module in src/fe_utils/ is ill-considered,
because several src/common/ modules have dependencies on it, meaning that
libpgcommon and libpgfeutils now have mutual dependencies. That makes it
pointless to have distinct libraries at all. The intended design is that
libpgcommon is lower-level than libpgfeutils, so only dependencies from
the latter to the former are acceptable.
We already have the precedent that fe_memutils and a couple of other
modules in src/common/ are frontend-only, so it's not stretching anything
out of whack to treat logging.c as a frontend-only module in src/common/.
To the extent that such modules help provide a common frontend/backend
environment for the rest of common/ to use, it's a reasonable design.
(logging.c does not yet provide an ereport() emulation, but one can
dream.)
Hence, move these files over, and revert basically all of the build-system
changes made by commit cc8d41511
. There are no places that need to grow
new dependencies on libpgcommon, further reinforcing the idea that this
is the right solution.
Discussion: https://postgr.es/m/a912ffff-f6e4-778a-c86a-cf5c47a12933@2ndquadrant.com
317 lines
6.1 KiB
C
317 lines
6.1 KiB
C
/*
|
|
* psql - the PostgreSQL interactive terminal
|
|
*
|
|
* Copyright (c) 2000-2019, PostgreSQL Global Development Group
|
|
*
|
|
* src/bin/psql/large_obj.c
|
|
*/
|
|
#include "postgres_fe.h"
|
|
#include "large_obj.h"
|
|
|
|
#include "settings.h"
|
|
#include "common.h"
|
|
|
|
#include "common/logging.h"
|
|
|
|
static void print_lo_result(const char *fmt,...) pg_attribute_printf(1, 2);
|
|
|
|
static void
|
|
print_lo_result(const char *fmt,...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (!pset.quiet)
|
|
{
|
|
if (pset.popt.topt.format == PRINT_HTML)
|
|
fputs("<p>", pset.queryFout);
|
|
|
|
va_start(ap, fmt);
|
|
vfprintf(pset.queryFout, fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (pset.popt.topt.format == PRINT_HTML)
|
|
fputs("</p>\n", pset.queryFout);
|
|
else
|
|
fputs("\n", pset.queryFout);
|
|
}
|
|
|
|
if (pset.logfile)
|
|
{
|
|
va_start(ap, fmt);
|
|
vfprintf(pset.logfile, fmt, ap);
|
|
va_end(ap);
|
|
fputs("\n", pset.logfile);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Prepare to do a large-object operation. We *must* be inside a transaction
|
|
* block for all these operations, so start one if needed.
|
|
*
|
|
* Returns true if okay, false if failed. *own_transaction is set to indicate
|
|
* if we started our own transaction or not.
|
|
*/
|
|
static bool
|
|
start_lo_xact(const char *operation, bool *own_transaction)
|
|
{
|
|
PGTransactionStatusType tstatus;
|
|
PGresult *res;
|
|
|
|
*own_transaction = false;
|
|
|
|
if (!pset.db)
|
|
{
|
|
pg_log_error("%s: not connected to a database", operation);
|
|
return false;
|
|
}
|
|
|
|
tstatus = PQtransactionStatus(pset.db);
|
|
|
|
switch (tstatus)
|
|
{
|
|
case PQTRANS_IDLE:
|
|
/* need to start our own xact */
|
|
if (!(res = PSQLexec("BEGIN")))
|
|
return false;
|
|
PQclear(res);
|
|
*own_transaction = true;
|
|
break;
|
|
case PQTRANS_INTRANS:
|
|
/* use the existing xact */
|
|
break;
|
|
case PQTRANS_INERROR:
|
|
pg_log_error("%s: current transaction is aborted", operation);
|
|
return false;
|
|
default:
|
|
pg_log_error("%s: unknown transaction status", operation);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Clean up after a successful LO operation
|
|
*/
|
|
static bool
|
|
finish_lo_xact(const char *operation, bool own_transaction)
|
|
{
|
|
PGresult *res;
|
|
|
|
if (own_transaction && pset.autocommit)
|
|
{
|
|
/* close out our own xact */
|
|
if (!(res = PSQLexec("COMMIT")))
|
|
{
|
|
res = PSQLexec("ROLLBACK");
|
|
PQclear(res);
|
|
return false;
|
|
}
|
|
PQclear(res);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Clean up after a failed LO operation
|
|
*/
|
|
static bool
|
|
fail_lo_xact(const char *operation, bool own_transaction)
|
|
{
|
|
PGresult *res;
|
|
|
|
if (own_transaction && pset.autocommit)
|
|
{
|
|
/* close out our own xact */
|
|
res = PSQLexec("ROLLBACK");
|
|
PQclear(res);
|
|
}
|
|
|
|
return false; /* always */
|
|
}
|
|
|
|
|
|
/*
|
|
* do_lo_export()
|
|
*
|
|
* Write a large object to a file
|
|
*/
|
|
bool
|
|
do_lo_export(const char *loid_arg, const char *filename_arg)
|
|
{
|
|
int status;
|
|
bool own_transaction;
|
|
|
|
if (!start_lo_xact("\\lo_export", &own_transaction))
|
|
return false;
|
|
|
|
SetCancelConn();
|
|
status = lo_export(pset.db, atooid(loid_arg), filename_arg);
|
|
ResetCancelConn();
|
|
|
|
/* of course this status is documented nowhere :( */
|
|
if (status != 1)
|
|
{
|
|
pg_log_info("%s", PQerrorMessage(pset.db));
|
|
return fail_lo_xact("\\lo_export", own_transaction);
|
|
}
|
|
|
|
if (!finish_lo_xact("\\lo_export", own_transaction))
|
|
return false;
|
|
|
|
print_lo_result("lo_export");
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* do_lo_import()
|
|
*
|
|
* Copy large object from file to database
|
|
*/
|
|
bool
|
|
do_lo_import(const char *filename_arg, const char *comment_arg)
|
|
{
|
|
PGresult *res;
|
|
Oid loid;
|
|
char oidbuf[32];
|
|
bool own_transaction;
|
|
|
|
if (!start_lo_xact("\\lo_import", &own_transaction))
|
|
return false;
|
|
|
|
SetCancelConn();
|
|
loid = lo_import(pset.db, filename_arg);
|
|
ResetCancelConn();
|
|
|
|
if (loid == InvalidOid)
|
|
{
|
|
pg_log_info("%s", PQerrorMessage(pset.db));
|
|
return fail_lo_xact("\\lo_import", own_transaction);
|
|
}
|
|
|
|
/* insert description if given */
|
|
if (comment_arg)
|
|
{
|
|
char *cmdbuf;
|
|
char *bufptr;
|
|
size_t slen = strlen(comment_arg);
|
|
|
|
cmdbuf = pg_malloc_extended(slen * 2 + 256, MCXT_ALLOC_NO_OOM);
|
|
if (!cmdbuf)
|
|
return fail_lo_xact("\\lo_import", own_transaction);
|
|
sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
|
|
bufptr = cmdbuf + strlen(cmdbuf);
|
|
bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
|
|
strcpy(bufptr, "'");
|
|
|
|
if (!(res = PSQLexec(cmdbuf)))
|
|
{
|
|
free(cmdbuf);
|
|
return fail_lo_xact("\\lo_import", own_transaction);
|
|
}
|
|
|
|
PQclear(res);
|
|
free(cmdbuf);
|
|
}
|
|
|
|
if (!finish_lo_xact("\\lo_import", own_transaction))
|
|
return false;
|
|
|
|
print_lo_result("lo_import %u", loid);
|
|
|
|
sprintf(oidbuf, "%u", loid);
|
|
SetVariable(pset.vars, "LASTOID", oidbuf);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* do_lo_unlink()
|
|
*
|
|
* removes a large object out of the database
|
|
*/
|
|
bool
|
|
do_lo_unlink(const char *loid_arg)
|
|
{
|
|
int status;
|
|
Oid loid = atooid(loid_arg);
|
|
bool own_transaction;
|
|
|
|
if (!start_lo_xact("\\lo_unlink", &own_transaction))
|
|
return false;
|
|
|
|
SetCancelConn();
|
|
status = lo_unlink(pset.db, loid);
|
|
ResetCancelConn();
|
|
|
|
if (status == -1)
|
|
{
|
|
pg_log_info("%s", PQerrorMessage(pset.db));
|
|
return fail_lo_xact("\\lo_unlink", own_transaction);
|
|
}
|
|
|
|
if (!finish_lo_xact("\\lo_unlink", own_transaction))
|
|
return false;
|
|
|
|
print_lo_result("lo_unlink %u", loid);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* do_lo_list()
|
|
*
|
|
* Show all large objects in database with comments
|
|
*/
|
|
bool
|
|
do_lo_list(void)
|
|
{
|
|
PGresult *res;
|
|
char buf[1024];
|
|
printQueryOpt myopt = pset.popt;
|
|
|
|
if (pset.sversion >= 90000)
|
|
{
|
|
snprintf(buf, sizeof(buf),
|
|
"SELECT oid as \"%s\",\n"
|
|
" pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
|
|
" pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
|
|
" FROM pg_catalog.pg_largeobject_metadata "
|
|
" ORDER BY oid",
|
|
gettext_noop("ID"),
|
|
gettext_noop("Owner"),
|
|
gettext_noop("Description"));
|
|
}
|
|
else
|
|
{
|
|
snprintf(buf, sizeof(buf),
|
|
"SELECT loid as \"%s\",\n"
|
|
" pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
|
|
"FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
|
|
"ORDER BY 1",
|
|
gettext_noop("ID"),
|
|
gettext_noop("Description"));
|
|
}
|
|
|
|
res = PSQLexec(buf);
|
|
if (!res)
|
|
return false;
|
|
|
|
myopt.topt.tuples_only = false;
|
|
myopt.nullPrint = NULL;
|
|
myopt.title = _("Large objects");
|
|
myopt.translate_header = true;
|
|
|
|
printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
|
|
|
|
PQclear(res);
|
|
return true;
|
|
}
|