fe-lobj.c 12.2 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * fe-lobj.c--
4
 *	  Front-end large object interface
5 6 7 8 9
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
10
 *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.17 1998/10/01 01:40:22 tgl Exp $
11 12 13
 *
 *-------------------------------------------------------------------------
 */
14 15 16 17 18

#include "libpq-fe.h"
#include "libpq-int.h"
#include "postgres.h"

Bruce Momjian's avatar
Bruce Momjian committed
19 20 21 22
#ifdef WIN32
#include "win32.h"
#include <io.h>
#else
23
#if !defined(NO_UNISTD_H)
Bruce Momjian's avatar
Bruce Momjian committed
24
#include <unistd.h>
Bruce Momjian's avatar
Bruce Momjian committed
25
#endif
26
#endif
27
#include <string.h>
28 29
#include <fcntl.h>
#include <sys/stat.h>
30

31
#include "libpq/libpq-fs.h"		/* must come after sys/stat.h */
32

33

34
#define LO_BUFSIZE		  1024
35

36
static int	lo_initialize(PGconn *conn);
Marc G. Fournier's avatar
Marc G. Fournier committed
37

38 39
/*
 * lo_open
40
 *	  opens an existing large object
41 42 43 44 45
 *
 * returns the file descriptor for use in later lo_* calls
 * return -1 upon failure.
 */
int
46
lo_open(PGconn *conn, Oid lobjId, int mode)
47
{
48 49 50 51
	int			fd;
	int			result_len;
	PQArgBlock	argv[2];
	PGresult   *res;
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

	argv[0].isint = 1;
	argv[0].len = 4;
	argv[0].u.integer = lobjId;

	argv[1].isint = 1;
	argv[1].len = 4;
	argv[1].u.integer = mode;

	if (conn->lobjfuncs == (PGlobjfuncs *) NULL)
	{
		if (lo_initialize(conn) < 0)
			return -1;
	}

	res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2);
	if (PQresultStatus(res) == PGRES_COMMAND_OK)
	{
		PQclear(res);
71

72 73 74 75 76 77 78
		/* have to do this to reset offset in shared fd cache */
		/* but only if fd is valid */
		if (fd >= 0 && lo_lseek(conn, fd, 0L, SEEK_SET) < 0)
			return -1;
		return fd;
	}
	else
79 80
	{
		PQclear(res);
81
		return -1;
82
	}
83 84 85 86
}

/*
 * lo_close
87
 *	  closes an existing large object
88 89 90 91 92
 *
 * returns 0 upon success
 * returns -1 upon failure.
 */
int
93
lo_close(PGconn *conn, int fd)
94
{
95 96 97 98
	PQArgBlock	argv[1];
	PGresult   *res;
	int			retval;
	int			result_len;
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

	if (conn->lobjfuncs == (PGlobjfuncs *) NULL)
	{
		if (lo_initialize(conn) < 0)
			return -1;
	}

	argv[0].isint = 1;
	argv[0].len = 4;
	argv[0].u.integer = fd;
	res = PQfn(conn, conn->lobjfuncs->fn_lo_close,
			   &retval, &result_len, 1, argv, 1);
	if (PQresultStatus(res) == PGRES_COMMAND_OK)
	{
		PQclear(res);
		return retval;
	}
	else
117 118
	{
		PQclear(res);
119
		return -1;
120
	}
121 122 123 124
}

/*
 * lo_read
125
 *	  read len bytes of the large object into buf
126 127 128 129 130 131
 *
 * returns the length of bytes read.
 * the CALLER must have allocated enough space to hold the result returned
 */

int
132
lo_read(PGconn *conn, int fd, char *buf, int len)
133
{
134 135 136
	PQArgBlock	argv[2];
	PGresult   *res;
	int			result_len;
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

	if (conn->lobjfuncs == (PGlobjfuncs *) NULL)
	{
		if (lo_initialize(conn) < 0)
			return -1;
	}

	argv[0].isint = 1;
	argv[0].len = 4;
	argv[0].u.integer = fd;

	argv[1].isint = 1;
	argv[1].len = 4;
	argv[1].u.integer = len;

	res = PQfn(conn, conn->lobjfuncs->fn_lo_read,
			   (int *) buf, &result_len, 0, argv, 2);
	if (PQresultStatus(res) == PGRES_COMMAND_OK)
	{
		PQclear(res);
		return result_len;
	}
	else
160 161
	{
		PQclear(res);
162
		return -1;
163
	}
164 165 166 167
}

/*
 * lo_write
168
 *	  write len bytes of buf into the large object fd
169 170 171
 *
 */
int
172
lo_write(PGconn *conn, int fd, char *buf, int len)
173
{
174 175 176 177
	PQArgBlock	argv[2];
	PGresult   *res;
	int			result_len;
	int			retval;
178 179 180 181 182 183

	if (conn->lobjfuncs == (PGlobjfuncs *) NULL)
	{
		if (lo_initialize(conn) < 0)
			return -1;
	}
184

185 186
	if (len <= 0)
		return 0;
187

188 189 190
	argv[0].isint = 1;
	argv[0].len = 4;
	argv[0].u.integer = fd;
191

192 193 194 195 196 197 198 199 200 201 202 203
	argv[1].isint = 0;
	argv[1].len = len;
	argv[1].u.ptr = (int *) buf;

	res = PQfn(conn, conn->lobjfuncs->fn_lo_write,
			   &retval, &result_len, 1, argv, 2);
	if (PQresultStatus(res) == PGRES_COMMAND_OK)
	{
		PQclear(res);
		return retval;
	}
	else
204 205
	{
		PQclear(res);
206
		return -1;
207
	}
208 209 210 211
}

/*
 * lo_lseek
212
 *	  change the current read or write location on a large object
213 214 215 216 217
 * currently, only L_SET is a legal value for whence
 *
 */

int
218
lo_lseek(PGconn *conn, int fd, int offset, int whence)
219
{
220 221 222 223
	PQArgBlock	argv[3];
	PGresult   *res;
	int			retval;
	int			result_len;
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250

	if (conn->lobjfuncs == (PGlobjfuncs *) NULL)
	{
		if (lo_initialize(conn) < 0)
			return -1;
	}

	argv[0].isint = 1;
	argv[0].len = 4;
	argv[0].u.integer = fd;

	argv[1].isint = 1;
	argv[1].len = 4;
	argv[1].u.integer = offset;

	argv[2].isint = 1;
	argv[2].len = 4;
	argv[2].u.integer = whence;

	res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek,
			   &retval, &result_len, 1, argv, 3);
	if (PQresultStatus(res) == PGRES_COMMAND_OK)
	{
		PQclear(res);
		return retval;
	}
	else
251 252
	{
		PQclear(res);
253
		return -1;
254
	}
255 256 257 258
}

/*
 * lo_creat
259
 *	  create a new large object
260 261 262 263 264 265 266
 * the mode is a bitmask describing different attributes of the new object
 *
 * returns the oid of the large object created or
 * InvalidOid upon failure
 */

Oid
267
lo_creat(PGconn *conn, int mode)
268
{
269 270 271 272
	PQArgBlock	argv[1];
	PGresult   *res;
	int			retval;
	int			result_len;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290

	if (conn->lobjfuncs == (PGlobjfuncs *) NULL)
	{
		if (lo_initialize(conn) < 0)
			return -1;
	}

	argv[0].isint = 1;
	argv[0].len = 4;
	argv[0].u.integer = mode;
	res = PQfn(conn, conn->lobjfuncs->fn_lo_creat,
			   &retval, &result_len, 1, argv, 1);
	if (PQresultStatus(res) == PGRES_COMMAND_OK)
	{
		PQclear(res);
		return (Oid) retval;
	}
	else
291 292
	{
		PQclear(res);
293
		return InvalidOid;
294
	}
295 296 297 298 299
}


/*
 * lo_tell
300
 *	  returns the current seek location of the large object
301 302 303 304
 *
 */

int
305
lo_tell(PGconn *conn, int fd)
306
{
307 308 309 310
	int			retval;
	PQArgBlock	argv[1];
	PGresult   *res;
	int			result_len;
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329

	if (conn->lobjfuncs == (PGlobjfuncs *) NULL)
	{
		if (lo_initialize(conn) < 0)
			return -1;
	}

	argv[0].isint = 1;
	argv[0].len = 4;
	argv[0].u.integer = fd;

	res = PQfn(conn, conn->lobjfuncs->fn_lo_tell,
			   &retval, &result_len, 1, argv, 1);
	if (PQresultStatus(res) == PGRES_COMMAND_OK)
	{
		PQclear(res);
		return retval;
	}
	else
330 331
	{
		PQclear(res);
332
		return -1;
333
	}
334 335 336 337
}

/*
 * lo_unlink
338
 *	  delete a file
339 340 341 342
 *
 */

int
343
lo_unlink(PGconn *conn, Oid lobjId)
344
{
345 346 347 348
	PQArgBlock	argv[1];
	PGresult   *res;
	int			result_len;
	int			retval;
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367

	if (conn->lobjfuncs == (PGlobjfuncs *) NULL)
	{
		if (lo_initialize(conn) < 0)
			return -1;
	}

	argv[0].isint = 1;
	argv[0].len = 4;
	argv[0].u.integer = lobjId;

	res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink,
			   &retval, &result_len, 1, argv, 1);
	if (PQresultStatus(res) == PGRES_COMMAND_OK)
	{
		PQclear(res);
		return retval;
	}
	else
368 369
	{
		PQclear(res);
370
		return -1;
371
	}
372 373 374 375
}

/*
 * lo_import -
376 377
 *	  imports a file as an (inversion) large object.
 *		returns the oid of that object upon success,
378 379 380 381 382
 * returns InvalidOid upon failure
 *
 */

Oid
383
lo_import(PGconn *conn, char *filename)
384
{
385 386 387 388 389 390
	int			fd;
	int			nbytes,
				tmp;
	char		buf[LO_BUFSIZE];
	Oid			lobjOid;
	int			lobj;
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439

	/*
	 * open the file to be read in
	 */
	fd = open(filename, O_RDONLY, 0666);
	if (fd < 0)
	{							/* error */
		sprintf(conn->errorMessage,
				"lo_import: can't open unix file\"%s\"\n", filename);
		return InvalidOid;
	}

	/*
	 * create an inversion "object"
	 */
	lobjOid = lo_creat(conn, INV_READ | INV_WRITE);
	if (lobjOid == InvalidOid)
	{
		sprintf(conn->errorMessage,
			  "lo_import: can't create inv object for \"%s\"", filename);
		return InvalidOid;
	}

	lobj = lo_open(conn, lobjOid, INV_WRITE);
	if (lobj == -1)
	{
		sprintf(conn->errorMessage,
				"lo_import: could not open inv object oid %d", lobjOid);
		return InvalidOid;
	}

	/*
	 * read in from the Unix file and write to the inversion file
	 */
	while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0)
	{
		tmp = lo_write(conn, lobj, buf, nbytes);
		if (tmp < nbytes)
		{
			sprintf(conn->errorMessage,
					"lo_import: error while reading \"%s\"", filename);
			return InvalidOid;
		}
	}

	(void) close(fd);
	(void) lo_close(conn, lobj);

	return lobjOid;
440 441 442 443
}

/*
 * lo_export -
444
 *	  exports an (inversion) large object.
445 446 447
 * returns -1 upon failure, 1 otherwise
 */
int
448
lo_export(PGconn *conn, Oid lobjId, char *filename)
449
{
450 451 452 453 454
	int			fd;
	int			nbytes,
				tmp;
	char		buf[LO_BUFSIZE];
	int			lobj;
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469

	/*
	 * create an inversion "object"
	 */
	lobj = lo_open(conn, lobjId, INV_READ);
	if (lobj == -1)
	{
		sprintf(conn->errorMessage,
				"lo_export: can't open inv object %d", lobjId);
		return -1;
	}

	/*
	 * open the file to be written to
	 */
470
	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
471 472 473 474 475 476
	if (fd < 0)
	{							/* error */
		sprintf(conn->errorMessage,
				"lo_export: can't open unix file\"%s\"", filename);
		return 0;
	}
477

478 479 480 481 482 483 484 485 486 487 488 489 490
	/*
	 * read in from the Unix file and write to the inversion file
	 */
	while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0)
	{
		tmp = write(fd, buf, nbytes);
		if (tmp < nbytes)
		{
			sprintf(conn->errorMessage,
					"lo_export: error while writing \"%s\"",
					filename);
			return -1;
		}
491 492
	}

493 494
	(void) lo_close(conn, lobj);
	(void) close(fd);
495

496
	return 1;
497
}
Marc G. Fournier's avatar
Marc G. Fournier committed
498 499 500 501 502 503 504 505 506 507


/* ----------------
 * lo_initialize
 *
 * Initialize the large object interface for an existing connection.
 * We ask the backend about the functions OID's in pg_proc for all
 * functions that are required for large object operations.
 * ----------------
 */
508
static int
509
lo_initialize(PGconn *conn)
Marc G. Fournier's avatar
Marc G. Fournier committed
510
{
511 512 513 514 515
	PGresult   *res;
	PGlobjfuncs *lobjfuncs;
	int			n;
	char	   *fname;
	Oid			foid;
516 517 518 519 520 521 522 523 524 525 526 527

	/* ----------------
	 * Allocate the structure to hold the functions OID's
	 * ----------------
	 */
	lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
	if (lobjfuncs == (PGlobjfuncs *) NULL)
	{
		strcpy(conn->errorMessage,
			   "FATAL: malloc() failed in lo_initialize()\n");
		return -1;
	}
Bruce Momjian's avatar
Bruce Momjian committed
528
	MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs));
529 530 531 532 533 534

	/* ----------------
	 * Execute the query to get all the functions at once
	 * ----------------
	 */
	res = PQexec(conn, "select proname, oid from pg_proc	\
Marc G. Fournier's avatar
Marc G. Fournier committed
535 536 537 538 539 540
    		where proname = 'lo_open'	\
		   or proname = 'lo_close'	\
		   or proname = 'lo_creat'	\
		   or proname = 'lo_unlink'	\
		   or proname = 'lo_lseek'	\
		   or proname = 'lo_tell'	\
541 542
		   or proname = 'loread'	\
		   or proname = 'lowrite'");
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
	if (res == (PGresult *) NULL)
	{
		free(lobjfuncs);
		return -1;
	}

	if (res->resultStatus != PGRES_TUPLES_OK)
	{
		free(lobjfuncs);
		PQclear(res);
		strcpy(conn->errorMessage,
			   "ERROR: SELECT didn't return data in lo_initialize()\n");
		return -1;
	}

	/* ----------------
	 * Examine the result and put the OID's into the struct
	 * ----------------
	 */
	for (n = 0; n < PQntuples(res); n++)
	{
		fname = PQgetvalue(res, n, 0);
		foid = (Oid) atoi(PQgetvalue(res, n, 1));
		if (!strcmp(fname, "lo_open"))
			lobjfuncs->fn_lo_open = foid;
		else if (!strcmp(fname, "lo_close"))
			lobjfuncs->fn_lo_close = foid;
		else if (!strcmp(fname, "lo_creat"))
			lobjfuncs->fn_lo_creat = foid;
		else if (!strcmp(fname, "lo_unlink"))
			lobjfuncs->fn_lo_unlink = foid;
		else if (!strcmp(fname, "lo_lseek"))
			lobjfuncs->fn_lo_lseek = foid;
		else if (!strcmp(fname, "lo_tell"))
			lobjfuncs->fn_lo_tell = foid;
		else if (!strcmp(fname, "loread"))
			lobjfuncs->fn_lo_read = foid;
		else if (!strcmp(fname, "lowrite"))
			lobjfuncs->fn_lo_write = foid;
	}
Marc G. Fournier's avatar
Marc G. Fournier committed
583 584 585

	PQclear(res);

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
	/* ----------------
	 * Finally check that we really got all large object
	 * interface functions.
	 * ----------------
	 */
	if (lobjfuncs->fn_lo_open == 0)
	{
		strcpy(conn->errorMessage,
			   "ERROR: Cannot determine OID for function lo_open\n");
		free(lobjfuncs);
		return -1;
	}
	if (lobjfuncs->fn_lo_close == 0)
	{
		strcpy(conn->errorMessage,
			   "ERROR: Cannot determine OID for function lo_close\n");
		free(lobjfuncs);
		return -1;
	}
	if (lobjfuncs->fn_lo_creat == 0)
	{
		strcpy(conn->errorMessage,
			   "ERROR: Cannot determine OID for function lo_creat\n");
		free(lobjfuncs);
		return -1;
	}
	if (lobjfuncs->fn_lo_unlink == 0)
	{
		strcpy(conn->errorMessage,
			   "ERROR: Cannot determine OID for function lo_unlink\n");
		free(lobjfuncs);
		return -1;
	}
	if (lobjfuncs->fn_lo_lseek == 0)
	{
		strcpy(conn->errorMessage,
			   "ERROR: Cannot determine OID for function lo_lseek\n");
		free(lobjfuncs);
		return -1;
	}
	if (lobjfuncs->fn_lo_tell == 0)
	{
		strcpy(conn->errorMessage,
			   "ERROR: Cannot determine OID for function lo_tell\n");
		free(lobjfuncs);
		return -1;
	}
	if (lobjfuncs->fn_lo_read == 0)
	{
		strcpy(conn->errorMessage,
			   "ERROR: Cannot determine OID for function loread\n");
		free(lobjfuncs);
		return -1;
	}
	if (lobjfuncs->fn_lo_write == 0)
	{
		strcpy(conn->errorMessage,
			   "ERROR: Cannot determine OID for function lowrite\n");
		free(lobjfuncs);
		return -1;
	}
Marc G. Fournier's avatar
Marc G. Fournier committed
647

648 649 650 651 652 653 654
	/* ----------------
	 * Put the structure into the connection control
	 * ----------------
	 */
	conn->lobjfuncs = lobjfuncs;
	return 0;
}