PostgreSQL7.0手册-接口-50. 函数 51. 大对象
用户可调用的函数的参考.
注意:本节需要人来写.愿当支援者吗?
第五十一章. 大对象
内容
历史信息
实现的特点
接口
内建的已注册函数
从 LIBPQ 里访问大对象
例子程序
在 Postgres 里,记录存储在数据页面里并且单个记录里的数据大小不能超过数据页面的大小.因为数据页面大小是 8192 字节,所以数据值的大小是相当小的.为了存储更大的不可分割数值(原子数值),Postgres 提供了大对象接口.这个接口给用户提供对定义为大对象的用户数据的面向文件的接口.本节描述 Postgres 大对象数据的实现,编程和查询语言接口.
历史信息
最初,Postgres 4.2 支持三种大对象的标准实现:作为 Postgres 的文件扩展,作为由 Postgres 管理的 UNIX 文件,以及作为存储在 Postgres 数据库里面的数据.这样做容易导致用户的迷惑.结果是,我们只支持把大对象作为数据存储在Postgres 数据库里,即使这样做令数据访问变得有些慢,但却保证了更严格的数据完整性.由于历史原因,这种存储机制被称为倒转大对象.(我们将在本章中交互使用倒转和大对象来表示同一个意思)。
--------------------------------------------------------------------------------
实现的特点
倒转大对象把大对象分解成"块" ("chunks"),然后把块存放在数据库记录里面.在随机读写时使用一个B-tree 索引保证对正确的块(chunk)号的检索.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
接口
Postgres 提供的用于访问大对象的机制,包括作为用户定义函数的后端的一部分或者作为使用接口的前端应用的一部分,都在下面描述.对于熟悉 Postgres 4.2 的用户,PostgreSQL 有一套新的函数提供更连贯的接口.
注意:所有大对象的操作都必须在一个SQL事务里实现。这在 Postgres v6.5 里有严格的要求,尽管在以前的版本里就隐含这样的要求,如果忽略这一点会导致错误的表现。
Postgres 大对象接口是对 UNIX 文件系统的模仿,有仿真的 open(2),read(2),write(2),lseek(2),等.用户函数调用这些路径从一个大对象中检索她们感兴趣的数据.例如,如果一个名为 mugshot 的大对象类型存在,在其中保存面孔的图象,那么可以在 mugshot 数据上定义一个叫 beard (胡子)的函数.Beard (胡子函数)会检查图片的下三分之一区域,并且如果存在胡子的话判断胡子的颜色.整个大对象的值不需要被 brard 函数缓存起来或者甚至是作些检查.大对象可以从动态装载的 C 函数或者是链接该库的数据库客户程序访问.Postgres 提供一套过程支持对大对象的打开,读,写,关闭和搜索。
创建大对象
过程
Oid lo_creat(PGconn *conn, int mode)
创建一个新的大对象.mode 是一个位掩码,描述新对象的不同属性.这里列出的符号常量在 $PGROOT/src/backend/libpq/libpq-fs.h.访问类型(读,写或者两者)是对位 INV_READ 和 INV_WRITE进行 OR (或)操作构成的.如果大对象应被归档--也就是说,如果因历史原因它应该被周期地移到一个特殊的归档关系(表)中--那么就要设置 INV_ARCHIVE 位.掩码的低十六位是大对象要存放于内的存储管理器号.对于除 Berkeley (伯克利)以外的节点,这些位都应总是零.下面的命令创建一个(倒转的)大对象:
inv_oid = lo_creat(INV_READINV_WRITEINV_ARCHIVE);
输入大对象
要把一个 UNIX 文件输入成为大对象,调用
Oid lo_import(PGconn *conn, const char *filename)
filename 参数指明要被输入成为大对象的 UNIX 文件路径名.
输出大对象
要把一个大对象输出为 UNIX 文件,调用
int lo_export(PGconn *conn, Oid lobjId, const char *filename)
lobjId 参数指明要输出的大对象 Oid,filename 参数指明 UNIX 文件的路径名.
打开一个现有的大对象
要打开一个现存的大对象,调用
int lo_open(PGconn *conn, Oid lobjId, int mode)
参数 lobjId 指明要打开的大对象的 Oid (对象标识).mode 位控制该对象是用于读(INV_READ),写还是读写.一个大对象在其创建之前不能被打开.lo_open 返回一个大对象标识用于以后的 lo_read,lo_write,lo_lseek,lo_tell,and lo_close。
向大对象中写数据
过程
int lo_write(PGconn *conn, int fd, const char *buf, size_t len)
从 buf 中向大对象 fd 中写 len 字节.参数 fd 必须是前面一个 lo_open 的返回.返回实际写的字节数.出错时返回负数.
从大对象中读取数据
过程
int lo_read(PGconn *conn, int fd, char *buf, size_t len)
从大对象中读取 len 字节数据到 buf 中。fd 参数必须是前面的一个 lo_open 调用的返回。返回实际读取的字节数。出错时,返回一个负数。
对大对象中数据的查找
要改变当前大对象的读或写位置,调用
int lo_lseek(PGconn *conn, int fd, int offset, int whence)
这个过程把当前 fd 代表的大对象位置指针移动到 offset 指明的新的位置.参数 whence 的合法的取值是 SEEK_SET SEEK_CUR 和 SEEK_END.
关闭一个大对象描述符
可以通过调用
int lo_close(PGconn *conn, int fd)
关闭一个大对象,这里 fd 是 lo_open 返回的大对象的描述符.成功时,lo_close 返回零.错误时,返回值是负数.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
内建的已注册函数
有两个内建的已注册函数,lo_import 和 lo_export 可以很方便的在 SQL 查询里面使用.下面是一些例子
CREATE TABLE image (
name text,
raster oid
);
INSERT INTO image (name, raster)
VALUES ('beautiful image', lo_import('/etc/motd'));
SELECT lo_export(image.raster, '/tmp/motd') from image
WHERE name = 'beautiful image';
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
通过 LIBPQ 访问大对象
下面是一个例子程序,显示如何使用 LIBPQ 里大对象接口的.程序的一部分是注释掉的,但仍然保留在源码里面供读者参考.这个程序可以在 ../src/test/examples 里找到.使用 LIBPQ 里大对象接口的前端应用应该包括头文件libpq/libpq-fs.h 并且联接 libpq 库.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
例子程序
/*--------------------------------------------------------------
*
* testlo.c--
* test using large objects with libpq
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* /usr/local/devel/pglite/cvs/src/doc/manual.me,v 1.16 1995/09/01 23:55:00 jolly Exp
*
*--------------------------------------------------------------
*/
#include
#include "libpq-fe.h"
#include "libpq/libpq-fs.h"
#define BUFSIZE 1024
/*
* importFile * import file "in_filename" into database as large object "lobjOid"
*
*/
Oid importFile(PGconn *conn, char *filename)
{
Oid lobjId;
int lobj_fd;
char buf[BUFSIZE];
int nbytes, tmp;
int fd;
/*
* open the file to be read in
*/
fd = open(filename, O_RDONLY, 0666);
if (fd < 0) { /* error */
fprintf(stderr, "can't open unix file %s\n", filename);
}
/*
* create the large object
*/
lobjId = lo_creat(conn, INV_READINV_WRITE);
if (lobjId == 0) {
fprintf(stderr, "can't create large object\n");
}
lobj_fd = lo_open(conn, lobjId, INV_WRITE);
/*
* read in from the Unix file and write to the inversion file
*/
while ((nbytes = read(fd, buf, BUFSIZE)) > 0) {
tmp = lo_write(conn, lobj_fd, buf, nbytes);
if (tmp < nbytes) {
fprintf(stderr, "error while reading large object\n");
}
}
(void) close(fd);
(void) lo_close(conn, lobj_fd);
return lobjId;
}
void pickout(PGconn *conn, Oid lobjId, int start, int len)
{
int lobj_fd;
char* buf;
int nbytes;
int nread;
lobj_fd = lo_open(conn, lobjId, INV_READ);
if (lobj_fd < 0) {
fprintf(stderr,"can't open large object %d\n",
lobjId);
}
lo_lseek(conn, lobj_fd, start, SEEK_SET);
buf = malloc(len+1);
nread = 0;
while (len - nread > 0) {
nbytes = lo_read(conn, lobj_fd, buf, len - nread);
buf[nbytes] = ' ';
fprintf(stderr,">>> %s", buf);
nread += nbytes;
}
fprintf(stderr,"\n");
lo_close(conn, lobj_fd);
}
void overwrite(PGconn *conn, Oid lobjId, int start, int len)
{
int lobj_fd;
char* buf;
int nbytes;
int nwritten;
int i;
lobj_fd = lo_open(conn, lobjId, INV_READ);
if (lobj_fd < 0) {
fprintf(stderr,"can't open large object %d\n",
lobjId);
}
lo_lseek(conn, lobj_fd, start, SEEK_SET);
buf = malloc(len+1);
for (i=0;i
buf[i] = ' ';
nwritten = 0;
while (len - nwritten > 0) {
nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten);
nwritten += nbytes;
}
fprintf(stderr,"\n");
lo_close(conn, lobj_fd);
}
/*
* exportFile * export large object "lobjOid" to file "out_filename"
*
*/
void exportFile(PGconn *conn, Oid lobjId, char *filename)
{
int lobj_fd;
char buf[BUFSIZE];
int nbytes, tmp;
int fd;
/*
* create an inversion "object"
*/
lobj_fd = lo_open(conn, lobjId, INV_READ);
if (lobj_fd < 0) {
fprintf(stderr,"can't open large object %d\n",
lobjId);
}
/*
* open the file to be written to
*/
fd = open(filename, O_CREATO_WRONLY, 0666);
if (fd < 0) { /* error */
fprintf(stderr, "can't open unix file %s\n",
filename);
}
/*
* read in from the Unix file and write to the inversion file
*/
while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0) {
tmp = write(fd, buf, nbytes);
if (tmp < nbytes) {
fprintf(stderr,"error while writing %s\n",
filename);
}
}
(void) lo_close(conn, lobj_fd);
(void) close(fd);
return;
}
void
exit_nicely(PGconn* conn)
{
PQfinish(conn);
exit(1);
}
int
main(int argc, char **argv)
{
char *in_filename, *out_filename;
char *database;
Oid lobjOid;
PGconn *conn;
PGresult *res;
if (argc != 4) {
fprintf(stderr, "Usage: %s database_name in_filename out_filename\n",
argv[0]);
exit(1);
}
database = argv[1];
in_filename = argv[2];
out_filename = argv[3];
/*
* set up the connection
*/
conn = PQsetdb(NULL, NULL, NULL, NULL, database);
/* check to see that the backend connection was successfully made */
if (PQstatus(conn) == CONNECTION_BAD) {
fprintf(stderr,"Connection to database '%s' failed.\n", database);
fprintf(stderr,"%s",PQerrorMessage(conn));
exit_nicely(conn);
}
res = PQexec(conn, "begin");
PQclear(res);
printf("importing file %s\n", in_filename);
/* lobjOid = importFile(conn, in_filename); */
lobjOid = lo_import(conn, in_filename);
/*
printf("as large object %d.\n", lobjOid);
printf("picking out bytes 1000-2000 of the large object\n");
pickout(conn, lobjOid, 1000, 1000);
printf("overwriting bytes 1000-2000 of the large object with X's\n");
overwrite(conn, lobjOid, 1000, 1000);
*/
printf("exporting large object to file %s\n", out_filename);
/* exportFile(conn, lobjOid, out_filename); */
lo_export(conn, lobjOid,out_filename);
res = PQexec(conn, "end");
PQclear(res);
PQfinish(conn);
exit(0);
}
------------------------------------------------------------------------------