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