diff --git a/contrib/README b/contrib/README index 773b0e30c3be677a8452f64d0dbeac9beaa17ae7..7dcd1ca1fe9fefbd043fa78e350d63224cdcc5d7 100644 --- a/contrib/README +++ b/contrib/README @@ -60,6 +60,9 @@ mSQL-interface - noupdate - trigger to prevent updates on single columns +pg_dumplo - + Dump large objects + soundex - Prototype for soundex function diff --git a/contrib/pg_dumplo/Makefile b/contrib/pg_dumplo/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0d4fb834243d987d03a21195b9445e025c7b1069 --- /dev/null +++ b/contrib/pg_dumplo/Makefile @@ -0,0 +1,26 @@ + +PROGRAM = pg_dumplo + +OBJECTS = pg_dumplo.o + +CFLAGS = -Wall -fpic -g +CC = gcc +RM = rm -f +INCLUDE = -I/usr/include/postgresql +LIBS =-lpq + +COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE) +LINK = $(CC) $(CFLAGS) -o $@ $(LIBS) + + +all: $(PROGRAM) + +$(PROGRAM): $(OBJECTS) + $(LINK) $(OBJECTS) + +.c.o: $< + $(COMPILE) -c $< + +clean: + $(RM) -f *~ $(OBJECTS) $(PROGRAM) + diff --git a/contrib/pg_dumplo/README b/contrib/pg_dumplo/README new file mode 100644 index 0000000000000000000000000000000000000000..b36cdd01d0bb14fc0a2c3b04672f53204aac174c --- /dev/null +++ b/contrib/pg_dumplo/README @@ -0,0 +1,18 @@ + + pg_dumplo - PostgreSQL large object dumper + + + Hmm... documentation is not available. For more information + see the help ( pg_dumplo -h ) and examples in this help. + + Compilation: + - you need the PostgreSQL's devel. libs + - and type: 'make' + + + Karel Zak <zakkr@zf.jcu.cz> + + + + + diff --git a/contrib/pg_dumplo/VERSION b/contrib/pg_dumplo/VERSION new file mode 100644 index 0000000000000000000000000000000000000000..05b19b1f76ec50c8a0345dbd528fa3defd3cbecc --- /dev/null +++ b/contrib/pg_dumplo/VERSION @@ -0,0 +1 @@ +0.0.4 \ No newline at end of file diff --git a/contrib/pg_dumplo/pg_dumplo.c b/contrib/pg_dumplo/pg_dumplo.c new file mode 100644 index 0000000000000000000000000000000000000000..6e6a20bef55b1496a843434537ce918809bb8e0b --- /dev/null +++ b/contrib/pg_dumplo/pg_dumplo.c @@ -0,0 +1,379 @@ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <time.h> + +#include <libpq-fe.h> +#include <libpq/libpq-fs.h> + +#define _GNU_SOURCE +#include <getopt.h> + +extern int errno; + +#define QUERY_BUFSIZ (8*1024) +#define DIR_UMASK 0755 +#define FILE_UMASK 0666 + +#define TRUE 1 +#define FALSE 0 +#define RE_OK 0 +#define RE_ERROR 1 + +typedef struct { + char *table, + *attr; + long lo_oid; +} lo_attr; + +void usage() +{ + printf("\nPostgreSQL large objects dump"); + printf("\npg_lo_dump <option>\n\n"); + printf("-h --help this help\n"); + printf("-u --user='username' username for connection to server\n"); + printf("-p --password='password' password for connection to server\n"); + printf("-d --db='database' database name\n"); + printf("-t --host='hostname' server hostname\n"); + printf("-l <table.attr ...> dump attribute (columns) with LO to dump tree\n"); + printf("-i --import import large obj dump tree to DB\n"); + printf("-s --space=<dir> directory with dupm tree (for dump/import)\n"); + printf("\nExample (dump): pg_lo_dump -d my_db -s /my_dump/dir/ -l t1.a t1.b t2.a\n"); + printf("Example (import): pg_lo_dump -i -d my_db -s /my_dump/dir/\n"); + printf("\nNote: * option '-l' must be last option!\n"); + printf(" * option '-i' (--import) make new large obj in DB, not rewrite old,\n"); + printf(" import UPDATE oid numbers in table.attr only.\n"); + printf("\n\n"); +} + +typedef enum { + ARG_USER, + ARG_PWD, + ARG_DB, + ARG_HELP, + ARG_HOST +} _ARG_; + +/*----- + * Init and allocate lo_attr structs + * + * ! data is **argv + *----- + */ +lo_attr *init_loa(char **data, int max, int start) +{ + lo_attr *l, + *ll; + char **d, + *loc, + buff[128]; + + if ((l = (lo_attr *) malloc(max * sizeof(lo_attr))) == NULL) { + fprintf(stderr, "%s: can't allocate memory\n", data[0]); + exit(RE_ERROR); + } + for(d=data+start, ll=l; *d != NULL; d++, ll++) { + strncpy(buff, *d, 128); + if ((loc = strchr(buff, '.')) == NULL) { + fprintf(stderr, "%s: '%s' is bad 'table.attr'\n", data[0], buff); + exit(RE_ERROR); + } + *loc = '\0'; + ll->table = strdup(buff); + ll->attr = strdup(++loc); + } + ll++; + ll->table = ll->attr = (char *) NULL; + return l; +} + +/*----- + * Check PG result + *----- + */ +int check_res(PGresult *res, PGconn *conn) +{ + if (!res && PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "%s\n",PQerrorMessage(conn)); + PQclear(res); + return FALSE; + } + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr, "Tuples is not OK.\n"); + PQclear(res); + return FALSE; + } + return TRUE; +} + + +/*----- + * LO dump + *----- + */ + void dump_lo(PGconn *conn, char *space, lo_attr *loa, char *db, char *prog) + { + PGresult *res; + lo_attr *ploa; + FILE *majorfile; + char *dir, + path[BUFSIZ], + Qbuff[QUERY_BUFSIZ]; + + dir = space ? space : getenv("PWD"); + sprintf(path, "%s/%s", dir, db); + if (mkdir(path, DIR_UMASK) == -1) { + if (errno != EEXIST) { + perror(path); + exit(RE_ERROR); + } + } + + sprintf(path, "%s/lo_dump.index", path); + if ((majorfile = fopen(path, "w")) == NULL) { + perror(path); + exit(RE_ERROR); + } else { + time_t t; + time(&t); + fprintf(majorfile, "#\n# This is the PostgreSQL large object dump index\n#\n"); + fprintf(majorfile, "#\tDate: %s", ctime(&t)); + fprintf(majorfile, "#\tHost: %s\n", PQhost(conn) ? PQhost(conn) : "localhost"); + fprintf(majorfile, "#\tDatabase: %s\n", db); + fprintf(majorfile, "#\tUser: %s\n", PQuser(conn)); + fprintf(majorfile, "#\n# oid\ttable\tattribut\tinfile\n"); + } + + for(ploa=loa; ploa->table != NULL; ploa++) { + + /* query */ + sprintf(Qbuff, "SELECT %s FROM %s WHERE %s!=0", + ploa->attr, ploa->table, ploa->attr); + + res = PQexec(conn, Qbuff); + + if (check_res(res, conn)) { + int tuples = PQntuples(res), + t; + char *val; + + /* Make DIR/FILE */ + if (tuples) { + sprintf(path, "%s/%s/%s", dir, db, ploa->table); + if (mkdir(path, DIR_UMASK) == -1) { + if (errno != EEXIST) { + perror(path); + exit(RE_ERROR); + } + } + sprintf(path, "%s/%s", path, ploa->attr); + if (mkdir(path, DIR_UMASK) == -1) { + if (errno != EEXIST) { + perror(path); + exit(RE_ERROR); + } + } + fprintf(stderr, "%s: dump %s.%s (%d lagre obj)\n", prog, + ploa->table, ploa->attr, tuples); + } + + for(t=0; t<tuples; t++) { + val = PQgetvalue(res, t, 0); + if (!val) + continue; + + sprintf(path, "%s/%s/%s/%s/%s", dir, db, ploa->table, ploa->attr, val); + + if (lo_export(conn, (Oid) atol(val), path) < 0) + fprintf(stderr, "%s\n", PQerrorMessage(conn)); + else + fprintf(majorfile, "%s\t%s\t%s\t%s/%s/%s/%s\n", val, + ploa->table, ploa->attr, db, ploa->table, ploa->attr, val); + } + } + } + fclose(majorfile); + } + + +/*----- + * LO import + *----- + */ + void import_lo(PGconn *conn, char *space, char *db, char *prog) + { + PGresult *res; + lo_attr loa; + FILE *majorfile; + long new_oid; + char *dir, + tab[128], attr[128], + path[BUFSIZ], lo_path[BUFSIZ], + Qbuff[QUERY_BUFSIZ]; + + dir = space ? space : getenv("PWD"); + sprintf(path, "%s/%s", dir, db); + + sprintf(path, "%s/lo_dump.index", path); + if ((majorfile = fopen(path, "r")) == NULL) { + perror(path); + exit(RE_ERROR); + } + + while(fgets(Qbuff, QUERY_BUFSIZ, majorfile)) { + + if (*Qbuff == '#') + continue; + + fprintf(stdout, Qbuff); + + sscanf(Qbuff, "%ld\t%s\t%s\t%s\n", &loa.lo_oid, tab, attr, path); + loa.table = tab; + loa.attr = attr; + + sprintf(lo_path, "%s/%s", dir, path); + + /* import large obj */ + if ((new_oid = lo_import(conn, lo_path)) <= 0) { + fprintf(stderr, "%s\n",PQerrorMessage(conn)); + PQexec(conn, "ROLLBACK"); + fprintf(stderr, "\nROLLBACK\n"); + exit(RE_ERROR); + } + + /* query */ + sprintf(Qbuff, "UPDATE %s SET %s=%ld WHERE %s=%ld", + loa.table, loa.attr, new_oid, loa.attr, loa.lo_oid); + + /*fprintf(stderr, Qbuff);*/ + + res = PQexec(conn, Qbuff); + + if (!res && PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "%s\n",PQerrorMessage(conn)); + PQclear(res); + PQexec(conn, "ROLLBACK"); + fprintf(stderr, "\nROLLBACK\n"); + exit(RE_ERROR); + } + + } + fclose(majorfile); + } + +/*----- + * The mother of all C functions + *----- + */ +int main(int argc, char **argv) +{ + PGconn *conn; + lo_attr *loa =NULL; + char *user =NULL, + *pwd =NULL, + *db =NULL, + *host =NULL, + *space =NULL; + int import =FALSE; + + /* Parse argv */ + if (argc) { + int arg, l_index=0; + extern int optind; + typedef enum { + ARG_USER, + ARG_PWD, + ARG_DB, + ARG_HELP, + ARG_IMPORT, + ARG_SPACE, + ARG_HOST + } _ARG_; + + struct option l_opt[] = { + { "help", 0, 0, ARG_HELP }, + { "user", 1, 0, ARG_USER }, + { "pwd", 1, 0, ARG_PWD }, + { "db", 1, 0, ARG_DB }, + { "host", 1, 0, ARG_HOST }, + { "space", 1, 0, ARG_SPACE }, + { "import", 0, 0, ARG_IMPORT }, + { NULL, 0, 0, 0 } + }; + + while((arg = getopt_long(argc, argv, "hu:p:d:l:t:is:", l_opt, &l_index)) != -1) { + switch(arg) { + case ARG_HELP: + case 'h': + usage(); + exit(RE_OK); + case ARG_USER: + case 'u': + user = strdup(optarg); + break; + case ARG_HOST: + case 't': + host = strdup(optarg); + break; + case ARG_PWD: + case 'p': + pwd = strdup(optarg); + break; + case ARG_DB: + case 'd': + db = strdup(optarg); + break; + case ARG_SPACE: + case 's': + space = strdup(optarg); + break; + case ARG_IMPORT: + case 'i': + import = TRUE; + break; + case 'l': + loa = init_loa(argv, argc-1, optind-1); + break; + } + } + } + + if (!space && !getenv("PWD")) { + fprintf(stderr, "%s: can't set directory (not set '-s' or $PWD).\n", argv[0]); + exit(RE_ERROR); + } + + /* Make PG connection */ + conn = PQsetdbLogin(host, NULL, NULL, NULL, db, user, pwd); + + /* check to see that the backend connection was successfully made */ + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr, "%s\n",PQerrorMessage(conn)); + exit(RE_ERROR); + } + + PQexec(conn, "BEGIN"); + + if (import) { + /* import obj */ + import_lo(conn, space, db, argv[0]); + } else if (loa) { + /* Dump obj */ + dump_lo(conn, space, loa, db, argv[0]); + } else { + fprintf(stderr, "%s: ERROR: bad arg!\n", argv[0]); + usage(); + } + + PQexec(conn, "COMMIT"); + + /* bye PG */ + PQfinish(conn); + exit(RE_OK); +} \ No newline at end of file