1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2018, 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 
12 #include "settings.h"
13 #include "common.h"
14 
15 static void print_lo_result(const char *fmt,...) pg_attribute_printf(1, 2);
16 
17 static void
print_lo_result(const char * fmt,...)18 print_lo_result(const char *fmt,...)
19 {
20 	va_list		ap;
21 
22 	if (!pset.quiet)
23 	{
24 		if (pset.popt.topt.format == PRINT_HTML)
25 			fputs("<p>", pset.queryFout);
26 
27 		va_start(ap, fmt);
28 		vfprintf(pset.queryFout, fmt, ap);
29 		va_end(ap);
30 
31 		if (pset.popt.topt.format == PRINT_HTML)
32 			fputs("</p>\n", pset.queryFout);
33 		else
34 			fputs("\n", pset.queryFout);
35 	}
36 
37 	if (pset.logfile)
38 	{
39 		va_start(ap, fmt);
40 		vfprintf(pset.logfile, fmt, ap);
41 		va_end(ap);
42 		fputs("\n", pset.logfile);
43 	}
44 }
45 
46 
47 /*
48  * Prepare to do a large-object operation.  We *must* be inside a transaction
49  * block for all these operations, so start one if needed.
50  *
51  * Returns true if okay, false if failed.  *own_transaction is set to indicate
52  * if we started our own transaction or not.
53  */
54 static bool
start_lo_xact(const char * operation,bool * own_transaction)55 start_lo_xact(const char *operation, bool *own_transaction)
56 {
57 	PGTransactionStatusType tstatus;
58 	PGresult   *res;
59 
60 	*own_transaction = false;
61 
62 	if (!pset.db)
63 	{
64 		psql_error("%s: not connected to a database\n", operation);
65 		return false;
66 	}
67 
68 	tstatus = PQtransactionStatus(pset.db);
69 
70 	switch (tstatus)
71 	{
72 		case PQTRANS_IDLE:
73 			/* need to start our own xact */
74 			if (!(res = PSQLexec("BEGIN")))
75 				return false;
76 			PQclear(res);
77 			*own_transaction = true;
78 			break;
79 		case PQTRANS_INTRANS:
80 			/* use the existing xact */
81 			break;
82 		case PQTRANS_INERROR:
83 			psql_error("%s: current transaction is aborted\n", operation);
84 			return false;
85 		default:
86 			psql_error("%s: unknown transaction status\n", operation);
87 			return false;
88 	}
89 
90 	return true;
91 }
92 
93 /*
94  * Clean up after a successful LO operation
95  */
96 static bool
finish_lo_xact(const char * operation,bool own_transaction)97 finish_lo_xact(const char *operation, bool own_transaction)
98 {
99 	PGresult   *res;
100 
101 	if (own_transaction && pset.autocommit)
102 	{
103 		/* close out our own xact */
104 		if (!(res = PSQLexec("COMMIT")))
105 		{
106 			res = PSQLexec("ROLLBACK");
107 			PQclear(res);
108 			return false;
109 		}
110 		PQclear(res);
111 	}
112 
113 	return true;
114 }
115 
116 /*
117  * Clean up after a failed LO operation
118  */
119 static bool
fail_lo_xact(const char * operation,bool own_transaction)120 fail_lo_xact(const char *operation, bool own_transaction)
121 {
122 	PGresult   *res;
123 
124 	if (own_transaction && pset.autocommit)
125 	{
126 		/* close out our own xact */
127 		res = PSQLexec("ROLLBACK");
128 		PQclear(res);
129 	}
130 
131 	return false;				/* always */
132 }
133 
134 
135 /*
136  * do_lo_export()
137  *
138  * Write a large object to a file
139  */
140 bool
do_lo_export(const char * loid_arg,const char * filename_arg)141 do_lo_export(const char *loid_arg, const char *filename_arg)
142 {
143 	int			status;
144 	bool		own_transaction;
145 
146 	if (!start_lo_xact("\\lo_export", &own_transaction))
147 		return false;
148 
149 	SetCancelConn();
150 	status = lo_export(pset.db, atooid(loid_arg), filename_arg);
151 	ResetCancelConn();
152 
153 	/* of course this status is documented nowhere :( */
154 	if (status != 1)
155 	{
156 		psql_error("%s", PQerrorMessage(pset.db));
157 		return fail_lo_xact("\\lo_export", own_transaction);
158 	}
159 
160 	if (!finish_lo_xact("\\lo_export", own_transaction))
161 		return false;
162 
163 	print_lo_result("lo_export");
164 
165 	return true;
166 }
167 
168 
169 /*
170  * do_lo_import()
171  *
172  * Copy large object from file to database
173  */
174 bool
do_lo_import(const char * filename_arg,const char * comment_arg)175 do_lo_import(const char *filename_arg, const char *comment_arg)
176 {
177 	PGresult   *res;
178 	Oid			loid;
179 	char		oidbuf[32];
180 	bool		own_transaction;
181 
182 	if (!start_lo_xact("\\lo_import", &own_transaction))
183 		return false;
184 
185 	SetCancelConn();
186 	loid = lo_import(pset.db, filename_arg);
187 	ResetCancelConn();
188 
189 	if (loid == InvalidOid)
190 	{
191 		psql_error("%s", PQerrorMessage(pset.db));
192 		return fail_lo_xact("\\lo_import", own_transaction);
193 	}
194 
195 	/* insert description if given */
196 	if (comment_arg)
197 	{
198 		char	   *cmdbuf;
199 		char	   *bufptr;
200 		size_t		slen = strlen(comment_arg);
201 
202 		cmdbuf = malloc(slen * 2 + 256);
203 		if (!cmdbuf)
204 			return fail_lo_xact("\\lo_import", own_transaction);
205 		sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
206 		bufptr = cmdbuf + strlen(cmdbuf);
207 		bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
208 		strcpy(bufptr, "'");
209 
210 		if (!(res = PSQLexec(cmdbuf)))
211 		{
212 			free(cmdbuf);
213 			return fail_lo_xact("\\lo_import", own_transaction);
214 		}
215 
216 		PQclear(res);
217 		free(cmdbuf);
218 	}
219 
220 	if (!finish_lo_xact("\\lo_import", own_transaction))
221 		return false;
222 
223 	print_lo_result("lo_import %u", loid);
224 
225 	sprintf(oidbuf, "%u", loid);
226 	SetVariable(pset.vars, "LASTOID", oidbuf);
227 
228 	return true;
229 }
230 
231 
232 /*
233  * do_lo_unlink()
234  *
235  * removes a large object out of the database
236  */
237 bool
do_lo_unlink(const char * loid_arg)238 do_lo_unlink(const char *loid_arg)
239 {
240 	int			status;
241 	Oid			loid = atooid(loid_arg);
242 	bool		own_transaction;
243 
244 	if (!start_lo_xact("\\lo_unlink", &own_transaction))
245 		return false;
246 
247 	SetCancelConn();
248 	status = lo_unlink(pset.db, loid);
249 	ResetCancelConn();
250 
251 	if (status == -1)
252 	{
253 		psql_error("%s", PQerrorMessage(pset.db));
254 		return fail_lo_xact("\\lo_unlink", own_transaction);
255 	}
256 
257 	if (!finish_lo_xact("\\lo_unlink", own_transaction))
258 		return false;
259 
260 	print_lo_result("lo_unlink %u", loid);
261 
262 	return true;
263 }
264 
265 
266 
267 /*
268  * do_lo_list()
269  *
270  * Show all large objects in database with comments
271  */
272 bool
do_lo_list(void)273 do_lo_list(void)
274 {
275 	PGresult   *res;
276 	char		buf[1024];
277 	printQueryOpt myopt = pset.popt;
278 
279 	if (pset.sversion >= 90000)
280 	{
281 		snprintf(buf, sizeof(buf),
282 				 "SELECT oid as \"%s\",\n"
283 				 "  pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
284 				 "  pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
285 				 "  FROM pg_catalog.pg_largeobject_metadata "
286 				 "  ORDER BY oid",
287 				 gettext_noop("ID"),
288 				 gettext_noop("Owner"),
289 				 gettext_noop("Description"));
290 	}
291 	else
292 	{
293 		snprintf(buf, sizeof(buf),
294 				 "SELECT loid as \"%s\",\n"
295 				 "  pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
296 				 "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
297 				 "ORDER BY 1",
298 				 gettext_noop("ID"),
299 				 gettext_noop("Description"));
300 	}
301 
302 	res = PSQLexec(buf);
303 	if (!res)
304 		return false;
305 
306 	myopt.topt.tuples_only = false;
307 	myopt.nullPrint = NULL;
308 	myopt.title = _("Large objects");
309 	myopt.translate_header = true;
310 
311 	printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
312 
313 	PQclear(res);
314 	return true;
315 }
316