Add error pattern checks for some TAP tests for non-existing objects
[pgsql.git] / src / bin / psql / large_obj.c
blob28f8c414b672c30d86d4232655b5041f130a5767
1 /*
2 * psql - the PostgreSQL interactive terminal
4 * Copyright (c) 2000-2025, PostgreSQL Global Development Group
6 * src/bin/psql/large_obj.c
7 */
8 #include "postgres_fe.h"
10 #include "common.h"
11 #include "common/logging.h"
12 #include "fe_utils/cancel.h"
13 #include "large_obj.h"
14 #include "settings.h"
16 static void print_lo_result(const char *fmt,...) pg_attribute_printf(1, 2);
18 static void
19 print_lo_result(const char *fmt,...)
21 va_list ap;
23 if (!pset.quiet)
25 if (pset.popt.topt.format == PRINT_HTML)
26 fputs("<p>", pset.queryFout);
28 va_start(ap, fmt);
29 vfprintf(pset.queryFout, fmt, ap);
30 va_end(ap);
32 if (pset.popt.topt.format == PRINT_HTML)
33 fputs("</p>\n", pset.queryFout);
34 else
35 fputs("\n", pset.queryFout);
38 if (pset.logfile)
40 va_start(ap, fmt);
41 vfprintf(pset.logfile, fmt, ap);
42 va_end(ap);
43 fputs("\n", pset.logfile);
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.
52 * Returns true if okay, false if failed. *own_transaction is set to indicate
53 * if we started our own transaction or not.
55 static bool
56 start_lo_xact(const char *operation, bool *own_transaction)
58 PGTransactionStatusType tstatus;
59 PGresult *res;
61 *own_transaction = false;
63 if (!pset.db)
65 pg_log_error("%s: not connected to a database", operation);
66 return false;
69 tstatus = PQtransactionStatus(pset.db);
71 switch (tstatus)
73 case PQTRANS_IDLE:
74 /* need to start our own xact */
75 if (!(res = PSQLexec("BEGIN")))
76 return false;
77 PQclear(res);
78 *own_transaction = true;
79 break;
80 case PQTRANS_INTRANS:
81 /* use the existing xact */
82 break;
83 case PQTRANS_INERROR:
84 pg_log_error("%s: current transaction is aborted", operation);
85 return false;
86 default:
87 pg_log_error("%s: unknown transaction status", operation);
88 return false;
91 return true;
95 * Clean up after a successful LO operation
97 static bool
98 finish_lo_xact(const char *operation, bool own_transaction)
100 PGresult *res;
102 if (own_transaction && pset.autocommit)
104 /* close out our own xact */
105 if (!(res = PSQLexec("COMMIT")))
107 res = PSQLexec("ROLLBACK");
108 PQclear(res);
109 return false;
111 PQclear(res);
114 return true;
118 * Clean up after a failed LO operation
120 static bool
121 fail_lo_xact(const char *operation, bool own_transaction)
123 PGresult *res;
125 if (own_transaction && pset.autocommit)
127 /* close out our own xact */
128 res = PSQLexec("ROLLBACK");
129 PQclear(res);
132 return false; /* always */
137 * do_lo_export()
139 * Write a large object to a file
141 bool
142 do_lo_export(const char *loid_arg, const char *filename_arg)
144 int status;
145 bool own_transaction;
147 if (!start_lo_xact("\\lo_export", &own_transaction))
148 return false;
150 SetCancelConn(NULL);
151 status = lo_export(pset.db, atooid(loid_arg), filename_arg);
152 ResetCancelConn();
154 /* of course this status is documented nowhere :( */
155 if (status != 1)
157 pg_log_info("%s", PQerrorMessage(pset.db));
158 return fail_lo_xact("\\lo_export", own_transaction);
161 if (!finish_lo_xact("\\lo_export", own_transaction))
162 return false;
164 print_lo_result("lo_export");
166 return true;
171 * do_lo_import()
173 * Copy large object from file to database
175 bool
176 do_lo_import(const char *filename_arg, const char *comment_arg)
178 PGresult *res;
179 Oid loid;
180 char oidbuf[32];
181 bool own_transaction;
183 if (!start_lo_xact("\\lo_import", &own_transaction))
184 return false;
186 SetCancelConn(NULL);
187 loid = lo_import(pset.db, filename_arg);
188 ResetCancelConn();
190 if (loid == InvalidOid)
192 pg_log_info("%s", PQerrorMessage(pset.db));
193 return fail_lo_xact("\\lo_import", own_transaction);
196 /* insert description if given */
197 if (comment_arg)
199 char *cmdbuf;
200 char *bufptr;
201 size_t slen = strlen(comment_arg);
203 cmdbuf = pg_malloc_extended(slen * 2 + 256, MCXT_ALLOC_NO_OOM);
204 if (!cmdbuf)
205 return fail_lo_xact("\\lo_import", own_transaction);
206 sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
207 bufptr = cmdbuf + strlen(cmdbuf);
208 bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
209 strcpy(bufptr, "'");
211 if (!(res = PSQLexec(cmdbuf)))
213 free(cmdbuf);
214 return fail_lo_xact("\\lo_import", own_transaction);
217 PQclear(res);
218 free(cmdbuf);
221 if (!finish_lo_xact("\\lo_import", own_transaction))
222 return false;
224 print_lo_result("lo_import %u", loid);
226 sprintf(oidbuf, "%u", loid);
227 SetVariable(pset.vars, "LASTOID", oidbuf);
229 return true;
234 * do_lo_unlink()
236 * removes a large object out of the database
238 bool
239 do_lo_unlink(const char *loid_arg)
241 int status;
242 Oid loid = atooid(loid_arg);
243 bool own_transaction;
245 if (!start_lo_xact("\\lo_unlink", &own_transaction))
246 return false;
248 SetCancelConn(NULL);
249 status = lo_unlink(pset.db, loid);
250 ResetCancelConn();
252 if (status == -1)
254 pg_log_info("%s", PQerrorMessage(pset.db));
255 return fail_lo_xact("\\lo_unlink", own_transaction);
258 if (!finish_lo_xact("\\lo_unlink", own_transaction))
259 return false;
261 print_lo_result("lo_unlink %u", loid);
263 return true;