Esempio n. 1
0
Datum
lo_close(PG_FUNCTION_ARGS)
{
	int32		fd = PG_GETARG_INT32(0);

#ifdef PGXC
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("Postgres-XC does not support large object yet"),
			 errdetail("The feature is not currently supported")));
#endif

	if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("invalid large-object descriptor: %d", fd)));

#if FSDB
	elog(DEBUG4, "lo_close(%d)", fd);
#endif

	inv_close(cookies[fd]);

	deleteLOfd(fd);

	PG_RETURN_INT32(0);
}
Esempio n. 2
0
/*
 * AtEOSubXact_LargeObject
 *		Take care of large objects at subtransaction commit/abort
 *
 * Reassign LOs created/opened during a committing subtransaction
 * to the parent subtransaction.  On abort, just close them.
 */
void
AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
						SubTransactionId parentSubid)
{
	int			i;

	if (fscxt == NULL)			/* no LO operations in this xact */
		return;

	for (i = 0; i < cookies_size; i++)
	{
		LargeObjectDesc *lo = cookies[i];

		if (lo != NULL && lo->subid == mySubid)
		{
			if (isCommit)
				lo->subid = parentSubid;
			else
			{
				/*
				 * Make sure we do not call inv_close twice if it errors out
				 * for some reason.  Better a leak than a crash.
				 */
				deleteLOfd(i);
				inv_close(lo);
			}
		}
	}
}
Esempio n. 3
0
/*
 * AtEOXact_LargeObject -
 *		 prepares large objects for transaction commit
 */
void
AtEOXact_LargeObject(bool isCommit)
{
	int			i;

	if (fscxt == NULL)
		return;					/* no LO operations in this xact */

	/*
	 * Close LO fds and clear cookies array so that LO fds are no longer good.
	 * On abort we skip the close step.
	 */
	for (i = 0; i < cookies_size; i++)
	{
		if (cookies[i] != NULL)
		{
			if (isCommit)
				inv_close(cookies[i]);
			deleteLOfd(i);
		}
	}

	/* Needn't actually pfree since we're about to zap context */
	cookies = NULL;
	cookies_size = 0;

	/* Release the LO memory context to prevent permanent memory leaks. */
	MemoryContextDelete(fscxt);
	fscxt = NULL;

	/* Give inv_api.c a chance to clean up, too */
	close_lo_relation(isCommit);
}
Esempio n. 4
0
/*
 * Update range within LO
 */
Datum
be_lo_put(PG_FUNCTION_ARGS)
{
	Oid			loOid = PG_GETARG_OID(0);
	int64		offset = PG_GETARG_INT64(1);
	bytea	   *str = PG_GETARG_BYTEA_PP(2);
	LargeObjectDesc *loDesc;
	int			written PG_USED_FOR_ASSERTS_ONLY;

	CreateFSContext();

	loDesc = inv_open(loOid, INV_WRITE, fscxt);

	/* Permission check */
	if (!lo_compat_privileges &&
		pg_largeobject_aclcheck_snapshot(loDesc->id,
										 GetUserId(),
										 ACL_UPDATE,
										 loDesc->snapshot) != ACLCHECK_OK)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied for large object %u",
						loDesc->id)));

	inv_seek(loDesc, offset, SEEK_SET);
	written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
	Assert(written == VARSIZE_ANY_EXHDR(str));
	inv_close(loDesc);

	PG_RETURN_VOID();
}
Esempio n. 5
0
Datum
lo_unlink(PG_FUNCTION_ARGS)
{
	Oid			lobjId = PG_GETARG_OID(0);

	/* Must be owner of the largeobject */
	if (!lo_compat_privileges &&
		!pg_largeobject_ownercheck(lobjId, GetUserId()))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be owner of large object %u", lobjId)));

	/*
	 * If there are any open LO FDs referencing that ID, close 'em.
	 */
	if (fscxt != NULL)
	{
		int			i;

		for (i = 0; i < cookies_size; i++)
		{
			if (cookies[i] != NULL && cookies[i]->id == lobjId)
			{
				inv_close(cookies[i]);
				deleteLOfd(i);
			}
		}
	}

	/*
	 * inv_drop does not create a need for end-of-transaction cleanup and
	 * hence we don't need to have created fscxt.
	 */
	PG_RETURN_INT32(inv_drop(lobjId));
}
Esempio n. 6
0
Datum
lo_unlink(PG_FUNCTION_ARGS)
{
	Oid			lobjId = PG_GETARG_OID(0);

	/*
	 * If there are any open LO FDs referencing that ID, close 'em.
	 */
	if (fscxt != NULL)
	{
		int			i;

		for (i = 0; i < cookies_size; i++)
		{
			if (cookies[i] != NULL && cookies[i]->id == lobjId)
			{
				inv_close(cookies[i]);
				deleteLOfd(i);
			}
		}
	}

	/*
	 * inv_drop does not create a need for end-of-transaction cleanup and
	 * hence we don't need to have created fscxt.
	 */
	PG_RETURN_INT32(inv_drop(lobjId));
}
Esempio n. 7
0
static Oid
lo_import_internal(text *filename, Oid lobjOid)
{
	File		fd;
	int			nbytes,
				tmp;
	char		buf[BUFSIZE];
	char		fnamebuf[MAXPGPATH];
	LargeObjectDesc *lobj;
	Oid			oid;

#ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser to use server-side lo_import()"),
				 errhint("Anyone can use the client-side lo_import() provided by libpq.")));
#endif

	CreateFSContext();

	/*
	 * open the file to be read in
	 */
	text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
	fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, S_IRWXU);
	if (fd < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not open server file \"%s\": %m",
						fnamebuf)));

	/*
	 * create an inversion object
	 */
	oid = inv_create(lobjOid);

	/*
	 * read in from the filesystem and write to the inversion object
	 */
	lobj = inv_open(oid, INV_WRITE, fscxt);

	while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0)
	{
		tmp = inv_write(lobj, buf, nbytes);
		Assert(tmp == nbytes);
	}

	if (nbytes < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not read server file \"%s\": %m",
						fnamebuf)));

	inv_close(lobj);
	FileClose(fd);

	return oid;
}
Esempio n. 8
0
/*
 * Read [offset, offset+nbytes) within LO; when nbytes is -1, read to end.
 */
static bytea *
lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
{
	LargeObjectDesc *loDesc;
	int64		loSize;
	int64		result_length;
	int			total_read PG_USED_FOR_ASSERTS_ONLY;
	bytea	   *result = NULL;

	/*
	 * We don't actually need to store into fscxt, but create it anyway to
	 * ensure that AtEOXact_LargeObject knows there is state to clean up
	 */
	CreateFSContext();

	loDesc = inv_open(loOid, INV_READ, fscxt);

	/*
	 * Compute number of bytes we'll actually read, accommodating nbytes == -1
	 * and reads beyond the end of the LO.
	 */
	loSize = inv_seek(loDesc, 0, SEEK_END);
	if (loSize > offset)
	{
		if (nbytes >= 0 && nbytes <= loSize - offset)
			result_length = nbytes; /* request is wholly inside LO */
		else
			result_length = loSize - offset;	/* adjust to end of LO */
	}
	else
		result_length = 0;		/* request is wholly outside LO */

	/*
	 * A result_length calculated from loSize may not fit in a size_t.  Check
	 * that the size will satisfy this and subsequently-enforced size limits.
	 */
	if (result_length > MaxAllocSize - VARHDRSZ)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("large object read request is too large")));

	result = (bytea *) palloc(VARHDRSZ + result_length);

	inv_seek(loDesc, offset, SEEK_SET);
	total_read = inv_read(loDesc, VARDATA(result), result_length);
	Assert(total_read == result_length);
	SET_VARSIZE(result, result_length + VARHDRSZ);

	inv_close(loDesc);

	return result;
}
Esempio n. 9
0
static Oid
lo_import_internal(text *filename, Oid lobjOid)
{
	int			fd;
	int			nbytes,
				tmp PG_USED_FOR_ASSERTS_ONLY;
	char		buf[BUFSIZE];
	char		fnamebuf[MAXPGPATH];
	LargeObjectDesc *lobj;
	Oid			oid;

	CreateFSContext();

	/*
	 * open the file to be read in
	 */
	text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
	fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY);
	if (fd < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not open server file \"%s\": %m",
						fnamebuf)));

	/*
	 * create an inversion object
	 */
	oid = inv_create(lobjOid);

	/*
	 * read in from the filesystem and write to the inversion object
	 */
	lobj = inv_open(oid, INV_WRITE, fscxt);

	while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
	{
		tmp = inv_write(lobj, buf, nbytes);
		Assert(tmp == nbytes);
	}

	if (nbytes < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not read server file \"%s\": %m",
						fnamebuf)));

	inv_close(lobj);
	CloseTransientFile(fd);

	return oid;
}
Esempio n. 10
0
Datum
lo_unlink(PG_FUNCTION_ARGS)
{
	Oid			lobjId = PG_GETARG_OID(0);

#ifdef PGXC
#ifdef XCP
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("Postgres-XL does not yet support large objects"),
			 errdetail("The feature is not currently supported")));
#else
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("Postgres-XC does not support large object yet"),
			 errdetail("The feature is not currently supported")));
#endif
#endif

	/* Must be owner of the largeobject */
	if (!lo_compat_privileges &&
		!pg_largeobject_ownercheck(lobjId, GetUserId()))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be owner of large object %u", lobjId)));

	/*
	 * If there are any open LO FDs referencing that ID, close 'em.
	 */
	if (fscxt != NULL)
	{
		int			i;

		for (i = 0; i < cookies_size; i++)
		{
			if (cookies[i] != NULL && cookies[i]->id == lobjId)
			{
				inv_close(cookies[i]);
				deleteLOfd(i);
			}
		}
	}

	/*
	 * inv_drop does not create a need for end-of-transaction cleanup and
	 * hence we don't need to have created fscxt.
	 */
	PG_RETURN_INT32(inv_drop(lobjId));
}
Esempio n. 11
0
intgen_t
query_test( int level )
{
	int i;
	inv_idbtoken_t tok;
	time_t *tm;
	inv_session_t *ses;
	invt_pr_ctx_t prctx;
	
	if (level == -2) {
		printf("mount pt %s\n",sesfile);
		tok = inv_open( INV_BY_MOUNTPT, INV_SEARCH_ONLY, sesfile );
		if (! tok ) return -1;
		idx_DEBUG_print (tok->d_invindex_fd);
		return 1;
	}
		
	for (i = 7; i<8; i++) {
		printf("\n\n\n----------------------------------\n"
		       "$ Searching fs %s\n", mnt_str[7-i] );
		tok = inv_open( INV_BY_MOUNTPT, INV_SEARCH_ONLY, mnt_str[7-i] );
		if (! tok ) return -1;

		prctx.index = i;
		if (level == -1 )
			invmgr_inv_print( tok->d_invindex_fd, &prctx );
		else {
		if (inv_lasttime_level_lessthan( tok, level, &tm ) && tm) {
			printf("\n\nTIME %s %ld\n", ctime(tm), (long) *tm );
			free (tm);
		}
		if (inv_lastsession_level_lessthan( tok, level, &ses ) && ses) {
			DEBUG_sessionprint( ses, 99, &prctx);
			free ( ses->s_streams );
			free ( ses );
		}

		if (inv_lastsession_level_equalto( tok, level, &ses ) && ses) {
			printf("Gotcha\n");
			DEBUG_sessionprint( ses, 99, &prctx );
			free ( ses->s_streams );
			free ( ses );
		}
	        }
		inv_close( tok );
	}
	return 1;
}				
Esempio n. 12
0
/*
 * Create LO with initial contents given by a bytea argument
 */
Datum
lo_from_bytea(PG_FUNCTION_ARGS)
{
	Oid			loOid = PG_GETARG_OID(0);
	bytea	   *str = PG_GETARG_BYTEA_PP(1);
	LargeObjectDesc *loDesc;
	int written PG_USED_FOR_ASSERTS_ONLY;

	CreateFSContext();

	loOid = inv_create(loOid);
	loDesc = inv_open(loOid, INV_WRITE, fscxt);
	written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
	Assert(written == VARSIZE_ANY_EXHDR(str));
	inv_close(loDesc);

	PG_RETURN_OID(loOid);
}
Esempio n. 13
0
/*
 * Class:     org_postgresql_pljava_internal_LargeObject
 * Method:    _close
 * Signature: (J)V
 */
JNIEXPORT void JNICALL
Java_org_postgresql_pljava_internal_LargeObject__1close(JNIEnv* env, jclass cls, jlong _this)
{
	LargeObjectDesc* self = Invocation_getWrappedPointer(_this);
	if(self != 0)
	{
		BEGIN_NATIVE
		PG_TRY();
		{
			inv_close(self);
		}
		PG_CATCH();
		{
			Exception_throw_ERROR("inv_close");
		}
		PG_END_TRY();
		END_NATIVE
	}
Esempio n. 14
0
/*
 * Update range within LO
 */
Datum
lo_put(PG_FUNCTION_ARGS)
{
	Oid			loOid = PG_GETARG_OID(0);
	int64		offset = PG_GETARG_INT64(1);
	bytea	   *str = PG_GETARG_BYTEA_PP(2);
	LargeObjectDesc *loDesc;
	int written PG_USED_FOR_ASSERTS_ONLY;

	CreateFSContext();

	loDesc = inv_open(loOid, INV_WRITE, fscxt);
	inv_seek(loDesc, offset, SEEK_SET);
	written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
	Assert(written == VARSIZE_ANY_EXHDR(str));
	inv_close(loDesc);

	PG_RETURN_VOID();
}
Esempio n. 15
0
Datum
lo_close(PG_FUNCTION_ARGS)
{
	int32		fd = PG_GETARG_INT32(0);

	if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("invalid large-object descriptor: %d", fd)));

#if FSDB
	elog(DEBUG4, "lo_close(%d)", fd);
#endif

	inv_close(cookies[fd]);

	deleteLOfd(fd);

	PG_RETURN_INT32(0);
}
Esempio n. 16
0
intgen_t
write_test( int nsess, int nstreams, int nmedia, int dumplevel )
{
	int i,j,k,m,fd;
	unsigned int stat;
	uuid_t *fsidp;
	inv_idbtoken_t tok1;
	inv_sestoken_t tok2;
	inv_stmtoken_t tok3;	
	char *dev, *mnt;
	char label[120];
	uuid_t fsidarr[8], labelid;
	uuid_t sesidarr[8];
	char *str;
	char strbuf[128];
	void *bufp;
	size_t sz;
	int rfd;

#ifdef FIRSTTIME
	printf("first time!\n");
	for (i=0; i<8; i++) {
		uuid_create( &fsidarr[i], &stat );
		ASSERT ( stat == uuid_s_ok );
		uuid_create( &sesidarr[i], &stat );
		ASSERT ( stat == uuid_s_ok );
	}
	fd = open( "uuids", O_RDWR | O_CREAT );
	PUT_REC(fd, (void *)fsidarr, sizeof (uuid_t) * 8, 0L );
	PUT_REC(fd, (void *)sesidarr, sizeof (uuid_t) * 8, sizeof (uuid_t) * 8 );
	close(fd);
#endif
	fd = open("uuids", O_RDONLY );
	GET_REC( fd, fsidarr, sizeof (uuid_t) * 8, 0L );
	GET_REC( fd, sesidarr, sizeof (uuid_t) * 8, sizeof (uuid_t) * 8 );
	close(fd);
#ifdef RECONS
	rfd = open( sesfile, O_RDWR | O_CREAT );
	fchmod( rfd, INV_PERMS );
#endif

	for ( i = 0; i < nsess; i++ ) {
		j = i % 8;
		/*mnt = mnt_str[j];
		dev = dev_str[7-j];*/
		mnt = mnt_str[0];
		dev = dev_str[7];
		fsidp = &fsidarr[0]; /* j */
		tok1 = inv_open( INV_BY_UUID, INV_SEARCH_N_MOD, fsidp );
		ASSERT (tok1 != INV_TOKEN_NULL );

		uuid_create( &labelid, &stat );
		uuid_to_string( &labelid, &str, &stat );
		strncpy( strbuf, str, 8 );
		free (str);
		strbuf[8] = '\0';
		sprintf(label,"%s_%s (%d)\0","SESSION_LABEL", strbuf, i );
		
		tok2 = inv_writesession_open(tok1, fsidp,
					     &labelid,
					     label, 
					     (bool_t)i%2,
					     (bool_t)i%2,
					     dumplevel, nstreams, 
					     time(NULL),
					     mnt, dev );
		ASSERT (tok2 != INV_TOKEN_NULL );
		for (m = 0; m<nstreams; m++) {
			tok3 = inv_stream_open( tok2,"/dev/rmt");
			ASSERT (tok3 != INV_TOKEN_NULL );

			for (k = 0; k<nmedia; k++ )
				CREAT_mfiles( tok3, &labelid, k*100,
					      k*100 + 99 );
			inv_stream_close( tok3, BOOL_TRUE );
		}
	
#ifdef RECONS
		if (inv_get_sessioninfo( tok2, &bufp, &sz ) == BOOL_TRUE ) {
		      put_invtrecord( rfd, fsidp, sizeof( uuid_t ), 0, 
				        SEEK_END, BOOL_FALSE );
			
			put_invtrecord( rfd, &sz, sizeof( size_t ), 0,
				        SEEK_END, BOOL_FALSE); 
			put_invtrecord( rfd, bufp, sz, 0, SEEK_END, BOOL_FALSE );
		}
#endif
#ifdef NOTDEF
		if (inv_get_sessioninfo( tok2, &bufp, &sz ) == BOOL_TRUE )
			inv_put_sessioninfo(  fsidp, bufp, sz );
#endif
		inv_writesession_close( tok2 );
		inv_close( tok1 );
	}	
#ifdef RECONS	
	close( rfd );
#endif
	return 1;
}
Esempio n. 17
0
/*
 * lo_export -
 *	  exports an (inversion) large object.
 */
Datum
lo_export(PG_FUNCTION_ARGS)
{
	Oid			lobjId = PG_GETARG_OID(0);
	text	   *filename = PG_GETARG_TEXT_PP(1);
	File		fd;
	int			nbytes,
				tmp;
	char		buf[BUFSIZE];
	char		fnamebuf[MAXPGPATH];
	LargeObjectDesc *lobj;
	mode_t		oumask;

#ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser to use server-side lo_export()"),
				 errhint("Anyone can use the client-side lo_export() provided by libpq.")));
#endif

	CreateFSContext();

	/*
	 * open the inversion object (no need to test for failure)
	 */
	lobj = inv_open(lobjId, INV_READ, fscxt);

	/*
	 * open the file to be written to
	 *
	 * Note: we reduce backend's normal 077 umask to the slightly friendlier
	 * 022. This code used to drop it all the way to 0, but creating
	 * world-writable export files doesn't seem wise.
	 */
	text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
	oumask = umask(S_IWGRP | S_IWOTH);
	fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
						  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
	umask(oumask);
	if (fd < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not create server file \"%s\": %m",
						fnamebuf)));

	/*
	 * read in from the inversion file and write to the filesystem
	 */
	while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
	{
		tmp = FileWrite(fd, buf, nbytes);
		if (tmp != nbytes)
			ereport(ERROR,
					(errcode_for_file_access(),
					 errmsg("could not write server file \"%s\": %m",
							fnamebuf)));
	}

	FileClose(fd);
	inv_close(lobj);

	PG_RETURN_INT32(1);
}
Esempio n. 18
0
/*
 * lo_export -
 *	  exports an (inversion) large object.
 */
Datum
be_lo_export(PG_FUNCTION_ARGS)
{
	Oid			lobjId = PG_GETARG_OID(0);
	text	   *filename = PG_GETARG_TEXT_PP(1);
	int			fd;
	int			nbytes,
				tmp;
	char		buf[BUFSIZE];
	char		fnamebuf[MAXPGPATH];
	LargeObjectDesc *lobj;
	mode_t		oumask;

	CreateFSContext();

	/*
	 * open the inversion object (no need to test for failure)
	 */
	lobj = inv_open(lobjId, INV_READ, fscxt);

	/*
	 * open the file to be written to
	 *
	 * Note: we reduce backend's normal 077 umask to the slightly friendlier
	 * 022. This code used to drop it all the way to 0, but creating
	 * world-writable export files doesn't seem wise.
	 */
	text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
	oumask = umask(S_IWGRP | S_IWOTH);
	PG_TRY();
	{
		fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
								   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
	}
	PG_CATCH();
	{
		umask(oumask);
		PG_RE_THROW();
	}
	PG_END_TRY();
	umask(oumask);
	if (fd < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not create server file \"%s\": %m",
						fnamebuf)));

	/*
	 * read in from the inversion file and write to the filesystem
	 */
	while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
	{
		tmp = write(fd, buf, nbytes);
		if (tmp != nbytes)
			ereport(ERROR,
					(errcode_for_file_access(),
					 errmsg("could not write server file \"%s\": %m",
							fnamebuf)));
	}

	CloseTransientFile(fd);
	inv_close(lobj);

	PG_RETURN_INT32(1);
}
Esempio n. 19
0
/*
 * lo_import -
 *	  imports a file as an (inversion) large object.
 */
Datum
lo_import(PG_FUNCTION_ARGS)
{
	text	   *filename = PG_GETARG_TEXT_P(0);
	File		fd;
	int			nbytes,
				tmp;
	char		buf[BUFSIZE];
	char		fnamebuf[MAXPGPATH];
	LargeObjectDesc *lobj;
	Oid			lobjOid;

#ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser to use server-side lo_import()"),
				 errhint("Anyone can use the client-side lo_import() provided by libpq.")));
#endif

	CreateFSContext();

	/*
	 * open the file to be read in
	 */
	nbytes = VARSIZE(filename) - VARHDRSZ;
	if (nbytes >= MAXPGPATH)
		nbytes = MAXPGPATH - 1;
	memcpy(fnamebuf, VARDATA(filename), nbytes);
	fnamebuf[nbytes] = '\0';
	fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666);
	if (fd < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not open server file \"%s\": %m",
						fnamebuf)));

	/*
	 * create an inversion object
	 */
	lobjOid = inv_create(InvalidOid);

	/*
	 * read in from the filesystem and write to the inversion object
	 */
	lobj = inv_open(lobjOid, INV_WRITE, fscxt);

	while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0)
	{
		tmp = inv_write(lobj, buf, nbytes);
		Assert(tmp == nbytes);
	}

	if (nbytes < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not read server file \"%s\": %m",
						fnamebuf)));

	inv_close(lobj);
	FileClose(fd);

	PG_RETURN_OID(lobjOid);
}