Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * psql - the PostgreSQL interactive terminal
3 : : *
4 : : * Copyright (c) 2000-2024, 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
6294 neilc@samurai.com 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)
6294 neilc@samurai.com 26 :UBC 0 : fputs("<p>", pset.queryFout);
27 : :
6294 neilc@samurai.com 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)
6294 neilc@samurai.com 33 :UBC 0 : fputs("</p>\n", pset.queryFout);
34 : : else
6294 neilc@samurai.com 35 :CBC 1 : fputs("\n", pset.queryFout);
36 : : }
37 : :
38 [ - + ]: 22 : if (pset.logfile)
39 : : {
6294 neilc@samurai.com 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 : : }
6294 neilc@samurai.com 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
7596 tgl@sss.pgh.pa.us 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 : : {
1840 peter@eisentraut.org 65 :UBC 0 : pg_log_error("%s: not connected to a database", operation);
7596 tgl@sss.pgh.pa.us 66 : 0 : return false;
67 : : }
68 : :
7596 tgl@sss.pgh.pa.us 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 */
3461 fujii@postgresql.org 75 [ - + ]: 22 : if (!(res = PSQLexec("BEGIN")))
7596 tgl@sss.pgh.pa.us 76 :UBC 0 : return false;
7596 tgl@sss.pgh.pa.us 77 :CBC 22 : PQclear(res);
78 : 22 : *own_transaction = true;
79 : 22 : break;
7596 tgl@sss.pgh.pa.us 80 :UBC 0 : case PQTRANS_INTRANS:
81 : : /* use the existing xact */
82 : 0 : break;
83 : 0 : case PQTRANS_INERROR:
1840 peter@eisentraut.org 84 : 0 : pg_log_error("%s: current transaction is aborted", operation);
7596 tgl@sss.pgh.pa.us 85 : 0 : return false;
86 : 0 : default:
1840 peter@eisentraut.org 87 : 0 : pg_log_error("%s: unknown transaction status", operation);
7596 tgl@sss.pgh.pa.us 88 : 0 : return false;
89 : : }
90 : :
7596 tgl@sss.pgh.pa.us 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 : :
6438 102 [ + - + - ]: 22 : if (own_transaction && pset.autocommit)
103 : : {
104 : : /* close out our own xact */
3461 fujii@postgresql.org 105 [ - + ]: 22 : if (!(res = PSQLexec("COMMIT")))
106 : : {
3461 fujii@postgresql.org 107 :UBC 0 : res = PSQLexec("ROLLBACK");
7596 tgl@sss.pgh.pa.us 108 : 0 : PQclear(res);
109 : 0 : return false;
110 : : }
7596 tgl@sss.pgh.pa.us 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
7596 tgl@sss.pgh.pa.us 121 :UBC 0 : fail_lo_xact(const char *operation, bool own_transaction)
122 : : {
123 : : PGresult *res;
124 : :
6438 125 [ # # # # ]: 0 : if (own_transaction && pset.autocommit)
126 : : {
127 : : /* close out our own xact */
3461 fujii@postgresql.org 128 : 0 : res = PSQLexec("ROLLBACK");
7596 tgl@sss.pgh.pa.us 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
8857 peter_e@gmx.net 142 :CBC 3 : do_lo_export(const char *loid_arg, const char *filename_arg)
143 : : {
144 : : int status;
145 : : bool own_transaction;
146 : :
7596 tgl@sss.pgh.pa.us 147 [ - + ]: 3 : if (!start_lo_xact("\\lo_export", &own_transaction))
8928 bruce@momjian.us 148 :UBC 0 : return false;
149 : :
1595 michael@paquier.xyz 150 :CBC 3 : SetCancelConn(NULL);
8228 tgl@sss.pgh.pa.us 151 : 3 : status = lo_export(pset.db, atooid(loid_arg), filename_arg);
6514 152 : 3 : ResetCancelConn();
153 : :
154 : : /* of course this status is documented nowhere :( */
8928 bruce@momjian.us 155 [ - + ]: 3 : if (status != 1)
156 : : {
1840 peter@eisentraut.org 157 :UBC 0 : pg_log_info("%s", PQerrorMessage(pset.db));
7596 tgl@sss.pgh.pa.us 158 : 0 : return fail_lo_xact("\\lo_export", own_transaction);
159 : : }
160 : :
7596 tgl@sss.pgh.pa.us 161 [ - + ]:CBC 3 : if (!finish_lo_xact("\\lo_export", own_transaction))
7596 tgl@sss.pgh.pa.us 162 :UBC 0 : return false;
163 : :
6294 neilc@samurai.com 164 :CBC 3 : print_lo_result("lo_export");
165 : :
8928 bruce@momjian.us 166 : 3 : return true;
167 : : }
168 : :
169 : :
170 : : /*
171 : : * do_lo_import()
172 : : *
173 : : * Copy large object from file to database
174 : : */
175 : : bool
8857 peter_e@gmx.net 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 : :
7596 tgl@sss.pgh.pa.us 183 [ - + ]: 7 : if (!start_lo_xact("\\lo_import", &own_transaction))
8928 bruce@momjian.us 184 :UBC 0 : return false;
185 : :
1595 michael@paquier.xyz 186 :CBC 7 : SetCancelConn(NULL);
8833 peter_e@gmx.net 187 : 7 : loid = lo_import(pset.db, filename_arg);
6514 tgl@sss.pgh.pa.us 188 : 7 : ResetCancelConn();
189 : :
8928 bruce@momjian.us 190 [ - + ]: 7 : if (loid == InvalidOid)
191 : : {
1840 peter@eisentraut.org 192 :UBC 0 : pg_log_info("%s", PQerrorMessage(pset.db));
7596 tgl@sss.pgh.pa.us 193 : 0 : return fail_lo_xact("\\lo_import", own_transaction);
194 : : }
195 : :
196 : : /* insert description if given */
7450 tgl@sss.pgh.pa.us 197 [ - + ]:CBC 7 : if (comment_arg)
198 : : {
199 : : char *cmdbuf;
200 : : char *bufptr;
7864 bruce@momjian.us 201 :UBC 0 : size_t slen = strlen(comment_arg);
202 : :
1807 michael@paquier.xyz 203 : 0 : cmdbuf = pg_malloc_extended(slen * 2 + 256, MCXT_ALLOC_NO_OOM);
3827 tgl@sss.pgh.pa.us 204 [ # # ]: 0 : if (!cmdbuf)
7596 205 : 0 : return fail_lo_xact("\\lo_import", own_transaction);
3827 206 : 0 : sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
207 : 0 : bufptr = cmdbuf + strlen(cmdbuf);
6531 208 : 0 : bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
7450 209 : 0 : strcpy(bufptr, "'");
210 : :
3461 fujii@postgresql.org 211 [ # # ]: 0 : if (!(res = PSQLexec(cmdbuf)))
212 : : {
8283 tgl@sss.pgh.pa.us 213 : 0 : free(cmdbuf);
7596 214 : 0 : return fail_lo_xact("\\lo_import", own_transaction);
215 : : }
216 : :
8283 217 : 0 : PQclear(res);
218 : 0 : free(cmdbuf);
219 : : }
220 : :
7596 tgl@sss.pgh.pa.us 221 [ - + ]:CBC 7 : if (!finish_lo_xact("\\lo_import", own_transaction))
7596 tgl@sss.pgh.pa.us 222 :UBC 0 : return false;
223 : :
6294 neilc@samurai.com 224 :CBC 7 : print_lo_result("lo_import %u", loid);
225 : :
8283 tgl@sss.pgh.pa.us 226 : 7 : sprintf(oidbuf, "%u", loid);
227 : 7 : SetVariable(pset.vars, "LASTOID", oidbuf);
228 : :
8928 bruce@momjian.us 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
8857 peter_e@gmx.net 239 : 12 : do_lo_unlink(const char *loid_arg)
240 : : {
241 : : int status;
8228 tgl@sss.pgh.pa.us 242 : 12 : Oid loid = atooid(loid_arg);
243 : : bool own_transaction;
244 : :
7596 245 [ - + ]: 12 : if (!start_lo_xact("\\lo_unlink", &own_transaction))
8928 bruce@momjian.us 246 :UBC 0 : return false;
247 : :
1595 michael@paquier.xyz 248 :CBC 12 : SetCancelConn(NULL);
8857 peter_e@gmx.net 249 : 12 : status = lo_unlink(pset.db, loid);
6514 tgl@sss.pgh.pa.us 250 : 12 : ResetCancelConn();
251 : :
8928 bruce@momjian.us 252 [ - + ]: 12 : if (status == -1)
253 : : {
1840 peter@eisentraut.org 254 :UBC 0 : pg_log_info("%s", PQerrorMessage(pset.db));
7596 tgl@sss.pgh.pa.us 255 : 0 : return fail_lo_xact("\\lo_unlink", own_transaction);
256 : : }
257 : :
7596 tgl@sss.pgh.pa.us 258 [ - + ]:CBC 12 : if (!finish_lo_xact("\\lo_unlink", own_transaction))
7596 tgl@sss.pgh.pa.us 259 :UBC 0 : return false;
260 : :
6294 neilc@samurai.com 261 :CBC 12 : print_lo_result("lo_unlink %u", loid);
262 : :
8928 bruce@momjian.us 263 : 12 : return true;
264 : : }
|