large_obj.c 6.99 KB
Newer Older
Peter Eisentraut's avatar
Peter Eisentraut committed
1 2 3
/*
 * psql - the PostgreSQL interactive terminal
 *
4
 * Copyright 2000-2002 by PostgreSQL Global Development Group
Peter Eisentraut's avatar
Peter Eisentraut committed
5
 *
6
 * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.25 2003/04/18 23:38:47 tgl Exp $
Peter Eisentraut's avatar
Peter Eisentraut committed
7
 */
8
#include "postgres_fe.h"
9 10
#include "large_obj.h"

11
#include "libpq-fe.h"
12 13 14 15 16 17 18

#include "settings.h"
#include "variables.h"
#include "common.h"
#include "print.h"


19 20 21
#define atooid(x)  ((Oid) strtoul((x), NULL, 10))


Bruce Momjian's avatar
Bruce Momjian committed
22
/*
23 24 25 26 27 28 29 30 31
 * Since all large object ops must be in a transaction, we must do some magic
 * here. You can set the variable lo_transaction to one of commit|rollback|
 * nothing to get your favourite behaviour regarding any transaction in
 * progress. Rollback is default.
 */

static char notice[80];

static void
Bruce Momjian's avatar
Bruce Momjian committed
32 33 34 35 36
_my_notice_handler(void *arg, const char *message)
{
	(void) arg;
	strncpy(notice, message, 79);
	notice[79] = '\0';
37 38 39 40
}


static bool
41
handle_transaction(void)
42
{
Bruce Momjian's avatar
Bruce Momjian committed
43
	PGresult   *res;
44
	bool		commit = false;
Bruce Momjian's avatar
Bruce Momjian committed
45
	PQnoticeProcessor old_notice_hook;
46

47
	switch (SwitchVariable(pset.vars, "LO_TRANSACTION", 
48 49 50
						   "nothing", 
						   "commit", 
						   NULL))
51
	{
52 53 54 55 56
		case 1:					/* nothing */
			return true;
		case 2:					/* commit */
			commit = true;
			break;
57
	}
Bruce Momjian's avatar
Bruce Momjian committed
58 59

	notice[0] = '\0';
60
	old_notice_hook = PQsetNoticeProcessor(pset.db, _my_notice_handler, NULL);
Bruce Momjian's avatar
Bruce Momjian committed
61

62
	res = PSQLexec(commit ? "COMMIT" : "ROLLBACK", false);
Bruce Momjian's avatar
Bruce Momjian committed
63 64
	if (!res)
		return false;
65

Bruce Momjian's avatar
Bruce Momjian committed
66 67
	if (notice[0])
	{
Bruce Momjian's avatar
Bruce Momjian committed
68 69
		if ((!commit && strcmp(notice, "WARNING:  ROLLBACK: no transaction in progress\n") != 0) ||
			(commit && strcmp(notice, "WARNING:  COMMIT: no transaction in progress\n") != 0))
Bruce Momjian's avatar
Bruce Momjian committed
70 71
			fputs(notice, stderr);
	}
72
	else if (!QUIET())
Bruce Momjian's avatar
Bruce Momjian committed
73 74
	{
		if (commit)
75
			puts(gettext("Warning: Your transaction in progress has been committed."));
Bruce Momjian's avatar
Bruce Momjian committed
76
		else
77
			puts(gettext("Warning: Your transaction in progress has been rolled back."));
Bruce Momjian's avatar
Bruce Momjian committed
78 79
	}

80
	PQsetNoticeProcessor(pset.db, old_notice_hook, NULL);
Bruce Momjian's avatar
Bruce Momjian committed
81
	return true;
82 83 84 85 86 87 88 89 90 91
}



/*
 * do_lo_export()
 *
 * Write a large object to a file
 */
bool
92
do_lo_export(const char *loid_arg, const char *filename_arg)
93
{
Bruce Momjian's avatar
Bruce Momjian committed
94 95
	PGresult   *res;
	int			status;
96
	bool		own_transaction;
Bruce Momjian's avatar
Bruce Momjian committed
97

98
	own_transaction = !VariableEquals(pset.vars, "LO_TRANSACTION", "nothing");
Bruce Momjian's avatar
Bruce Momjian committed
99

100
	if (!pset.db)
Bruce Momjian's avatar
Bruce Momjian committed
101
	{
102
		psql_error("\\lo_export: not connected to a database\n");
Bruce Momjian's avatar
Bruce Momjian committed
103 104
		return false;
	}
105

Bruce Momjian's avatar
Bruce Momjian committed
106 107
	if (own_transaction)
	{
108
		if (!handle_transaction())
Bruce Momjian's avatar
Bruce Momjian committed
109
			return false;
110

111
		if (!(res = PSQLexec("BEGIN", false)))
Bruce Momjian's avatar
Bruce Momjian committed
112
			return false;
113

Bruce Momjian's avatar
Bruce Momjian committed
114 115
		PQclear(res);
	}
116

117
	status = lo_export(pset.db, atooid(loid_arg), filename_arg);
Bruce Momjian's avatar
Bruce Momjian committed
118 119 120
	if (status != 1)
	{							/* of course this status is documented
								 * nowhere :( */
121
		fputs(PQerrorMessage(pset.db), stderr);
Bruce Momjian's avatar
Bruce Momjian committed
122 123
		if (own_transaction)
		{
124
			res = PQexec(pset.db, "ROLLBACK");
Bruce Momjian's avatar
Bruce Momjian committed
125 126 127
			PQclear(res);
		}
		return false;
128
	}
Bruce Momjian's avatar
Bruce Momjian committed
129 130 131

	if (own_transaction)
	{
132
		if (!(res = PSQLexec("COMMIT", false)))
Bruce Momjian's avatar
Bruce Momjian committed
133
		{
134
			res = PQexec(pset.db, "ROLLBACK");
Bruce Momjian's avatar
Bruce Momjian committed
135 136 137 138 139
			PQclear(res);
			return false;
		}

		PQclear(res);
140 141
	}

142
	fprintf(pset.queryFout, "lo_export\n");
143

Bruce Momjian's avatar
Bruce Momjian committed
144
	return true;
145 146 147 148 149 150 151 152 153 154
}



/*
 * do_lo_import()
 *
 * Copy large object from file to database
 */
bool
155
do_lo_import(const char *filename_arg, const char *comment_arg)
156
{
Bruce Momjian's avatar
Bruce Momjian committed
157 158
	PGresult   *res;
	Oid			loid;
159
	char		oidbuf[32];
Bruce Momjian's avatar
Bruce Momjian committed
160
	unsigned int i;
161
	bool		own_transaction;
Bruce Momjian's avatar
Bruce Momjian committed
162

163
	own_transaction = !VariableEquals(pset.vars, "LO_TRANSACTION", "nothing");
Bruce Momjian's avatar
Bruce Momjian committed
164

165
	if (!pset.db)
Bruce Momjian's avatar
Bruce Momjian committed
166
	{
167
		psql_error("\\lo_import: not connected to a database\n");
Bruce Momjian's avatar
Bruce Momjian committed
168 169
		return false;
	}
170

Bruce Momjian's avatar
Bruce Momjian committed
171 172
	if (own_transaction)
	{
173
		if (!handle_transaction())
Bruce Momjian's avatar
Bruce Momjian committed
174
			return false;
175

176
		if (!(res = PSQLexec("BEGIN", false)))
Bruce Momjian's avatar
Bruce Momjian committed
177
			return false;
178 179 180 181

		PQclear(res);
	}

182
	loid = lo_import(pset.db, filename_arg);
Bruce Momjian's avatar
Bruce Momjian committed
183 184
	if (loid == InvalidOid)
	{
185
		fputs(PQerrorMessage(pset.db), stderr);
Bruce Momjian's avatar
Bruce Momjian committed
186 187
		if (own_transaction)
		{
188
			res = PQexec(pset.db, "ROLLBACK");
Bruce Momjian's avatar
Bruce Momjian committed
189 190 191
			PQclear(res);
		}
		return false;
192 193
	}

Bruce Momjian's avatar
Bruce Momjian committed
194
	/* insert description if given */
195 196 197
	/* XXX don't try to hack pg_description if not superuser */
	/* XXX ought to replace this with some kind of COMMENT command */
	if (comment_arg && pset.issuper)
Bruce Momjian's avatar
Bruce Momjian committed
198
	{
199 200
		char	   *cmdbuf;
		char	   *bufptr;
201
		size_t		slen = strlen(comment_arg);
202 203 204

		cmdbuf = malloc(slen * 2 + 256);
		if (!cmdbuf)
Bruce Momjian's avatar
Bruce Momjian committed
205 206 207
		{
			if (own_transaction)
			{
208
				res = PQexec(pset.db, "ROLLBACK");
Bruce Momjian's avatar
Bruce Momjian committed
209 210 211 212
				PQclear(res);
			}
			return false;
		}
213
		sprintf(cmdbuf,
214 215 216 217
				"INSERT INTO pg_catalog.pg_description VALUES ('%u', "
				"'pg_catalog.pg_largeobject'::regclass, "
				"0, '",
				loid);
218 219 220 221 222 223 224 225 226
		bufptr = cmdbuf + strlen(cmdbuf);
		for (i = 0; i < slen; i++)
		{
			if (comment_arg[i] == '\'' || comment_arg[i] == '\\')
				*bufptr++ = '\\';
			*bufptr++ = comment_arg[i];
		}
		strcpy(bufptr, "')");

227
		if (!(res = PSQLexec(cmdbuf, false)))
228 229 230 231 232 233 234 235 236 237 238 239
		{
			if (own_transaction)
			{
				res = PQexec(pset.db, "ROLLBACK");
				PQclear(res);
			}
			free(cmdbuf);
			return false;
		}

		PQclear(res);
		free(cmdbuf);
Bruce Momjian's avatar
Bruce Momjian committed
240
	}
241

Bruce Momjian's avatar
Bruce Momjian committed
242 243
	if (own_transaction)
	{
244
		if (!(res = PSQLexec("COMMIT", false)))
Bruce Momjian's avatar
Bruce Momjian committed
245
		{
246
			res = PQexec(pset.db, "ROLLBACK");
Bruce Momjian's avatar
Bruce Momjian committed
247 248 249
			PQclear(res);
			return false;
		}
250

Bruce Momjian's avatar
Bruce Momjian committed
251 252
		PQclear(res);
	}
253

Bruce Momjian's avatar
Bruce Momjian committed
254

255 256 257
	fprintf(pset.queryFout, "lo_import %u\n", loid);
	sprintf(oidbuf, "%u", loid);
	SetVariable(pset.vars, "LASTOID", oidbuf);
Bruce Momjian's avatar
Bruce Momjian committed
258 259

	return true;
260 261 262 263 264 265 266 267 268
}



/*
 * do_lo_unlink()
 *
 * removes a large object out of the database
 */
Bruce Momjian's avatar
Bruce Momjian committed
269
bool
270
do_lo_unlink(const char *loid_arg)
271
{
Bruce Momjian's avatar
Bruce Momjian committed
272 273
	PGresult   *res;
	int			status;
274
	Oid			loid = atooid(loid_arg);
Bruce Momjian's avatar
Bruce Momjian committed
275
	char		buf[256];
276 277
	bool		own_transaction;

278
	own_transaction = !VariableEquals(pset.vars, "LO_TRANSACTION", "nothing");
Bruce Momjian's avatar
Bruce Momjian committed
279

280
	if (!pset.db)
Bruce Momjian's avatar
Bruce Momjian committed
281
	{
282
		psql_error("\\lo_unlink: not connected to a database\n");
Bruce Momjian's avatar
Bruce Momjian committed
283 284
		return false;
	}
285

Bruce Momjian's avatar
Bruce Momjian committed
286 287
	if (own_transaction)
	{
288
		if (!handle_transaction())
Bruce Momjian's avatar
Bruce Momjian committed
289
			return false;
290

291
		if (!(res = PSQLexec("BEGIN", false)))
Bruce Momjian's avatar
Bruce Momjian committed
292
			return false;
293

Bruce Momjian's avatar
Bruce Momjian committed
294 295
		PQclear(res);
	}
296

297
	status = lo_unlink(pset.db, loid);
Bruce Momjian's avatar
Bruce Momjian committed
298 299
	if (status == -1)
	{
300
		fputs(PQerrorMessage(pset.db), stderr);
Bruce Momjian's avatar
Bruce Momjian committed
301 302
		if (own_transaction)
		{
303
			res = PQexec(pset.db, "ROLLBACK");
Bruce Momjian's avatar
Bruce Momjian committed
304 305 306
			PQclear(res);
		}
		return false;
307
	}
Bruce Momjian's avatar
Bruce Momjian committed
308 309

	/* remove the comment as well */
310 311 312
	/* XXX don't try to hack pg_description if not superuser */
	/* XXX ought to replace this with some kind of COMMENT command */
	if (pset.issuper)
Bruce Momjian's avatar
Bruce Momjian committed
313
	{
314 315 316 317
		snprintf(buf, sizeof(buf),
				 "DELETE FROM pg_catalog.pg_description WHERE objoid = '%u' "
				 "AND classoid = 'pg_catalog.pg_largeobject'::regclass",
				 loid);
318
		if (!(res = PSQLexec(buf, false)))
Bruce Momjian's avatar
Bruce Momjian committed
319
		{
320 321 322 323 324 325
			if (own_transaction)
			{
				res = PQexec(pset.db, "ROLLBACK");
				PQclear(res);
			}
			return false;
Bruce Momjian's avatar
Bruce Momjian committed
326
		}
327 328
	}

Bruce Momjian's avatar
Bruce Momjian committed
329 330
	if (own_transaction)
	{
331
		if (!(res = PSQLexec("COMMIT", false)))
Bruce Momjian's avatar
Bruce Momjian committed
332
		{
333
			res = PQexec(pset.db, "ROLLBACK");
Bruce Momjian's avatar
Bruce Momjian committed
334 335 336 337
			PQclear(res);
			return false;
		}
		PQclear(res);
338 339 340
	}


341
	fprintf(pset.queryFout, "lo_unlink %u\n", loid);
342

Bruce Momjian's avatar
Bruce Momjian committed
343
	return true;
344 345 346 347 348 349 350
}



/*
 * do_lo_list()
 *
351
 * Show all large objects in database with comments
352
 */
Bruce Momjian's avatar
Bruce Momjian committed
353
bool
354
do_lo_list(void)
355
{
Bruce Momjian's avatar
Bruce Momjian committed
356
	PGresult   *res;
357
	char		buf[1024];
358
	printQueryOpt myopt = pset.popt;
Bruce Momjian's avatar
Bruce Momjian committed
359

360
	snprintf(buf, sizeof(buf),
361
			 "SELECT loid as \"ID\", pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
Bruce Momjian's avatar
Bruce Momjian committed
362
		 "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
363 364
			 "ORDER BY \"ID\"",
			 gettext("Description"));
365

366
	res = PSQLexec(buf, false);
Bruce Momjian's avatar
Bruce Momjian committed
367 368
	if (!res)
		return false;
369

Bruce Momjian's avatar
Bruce Momjian committed
370 371
	myopt.topt.tuples_only = false;
	myopt.nullPrint = NULL;
372
	myopt.title = gettext("Large objects");
373

374
	printQuery(res, &myopt, pset.queryFout);
375

Bruce Momjian's avatar
Bruce Momjian committed
376 377
	PQclear(res);
	return true;
378
}