]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/pg_backup_db.c
- Added CVS headers to files
[postgresql] / src / bin / pg_dump / pg_backup_db.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_backup_db.c
4  *
5  *  Implements the basic DB functions used by the archiver.
6  *
7  * IDENTIFICATION
8  *    $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.15 2001/03/19 02:35:28 pjw Exp $
9  *
10  * NOTES
11  *
12  * Modifications - 04-Jan-2001 - pjw@rhyme.com.au
13  *
14  *    - Check results of PQ routines more carefully.
15  *
16  * Modifications - 19-Mar-2001 - pjw@rhyme.com.au
17  *
18  *        - Avoid forcing table name to lower case in FixupBlobXrefs!
19  *
20  *-------------------------------------------------------------------------
21  */
22
23 #include "pg_dump.h"
24 #include "pg_backup.h"
25 #include "pg_backup_archiver.h"
26 #include "pg_backup_db.h"
27
28 #include <unistd.h>                             /* for getopt() */
29 #include <ctype.h>
30
31 #ifdef HAVE_TERMIOS_H
32 #include <termios.h>
33 #endif
34
35 #include "libpq-fe.h"
36 #include "libpq/libpq-fs.h"
37 #ifndef HAVE_STRDUP
38 #include "strdup.h"
39 #endif
40
41 static const char       *progname = "Archiver(db)";
42
43 static void     _prompt_for_password(char *username, char *password);
44 static void     _check_database_version(ArchiveHandle *AH, bool ignoreVersion);
45 static PGconn*  _connectDB(ArchiveHandle *AH, const char* newdbname, char *newUser);
46 static int              _executeSqlCommand(ArchiveHandle* AH, PGconn *conn, PQExpBuffer qry, char *desc);
47
48
49 static void
50 _prompt_for_password(char *username, char *password)
51 {
52         char            buf[512];
53         int                     length;
54         int                     buflen;
55
56 #ifdef HAVE_TERMIOS_H
57         struct termios t_orig,
58                                    t;
59 #endif
60
61         /*
62          * Allow for forcing a specific username
63          */
64         if (strlen(username) == 0)
65         {
66                 fprintf(stderr, "Username: ");
67                 fflush(stderr);
68                 if (fgets(username, 100, stdin) == NULL)
69                         username[0] = '\0';
70                 length = strlen(username);
71                 if (length > 0 && username[length - 1] != '\n')
72                 {
73                         /* eat rest of the line */
74                         do
75                         {
76                                 if (fgets(buf, sizeof(buf), stdin) == NULL)
77                                         break;
78                                 buflen = strlen(buf);
79                         } while (buflen > 0 && buf[buflen - 1] != '\n');
80                 }
81                 if (length > 0 && username[length - 1] == '\n')
82                         username[length - 1] = '\0';
83         }
84
85 #ifdef HAVE_TERMIOS_H
86         tcgetattr(0, &t);
87         t_orig = t;
88         t.c_lflag &= ~ECHO;
89         tcsetattr(0, TCSADRAIN, &t);
90 #endif
91         fprintf(stderr, "Password: ");
92         fflush(stderr);
93         if (fgets(password, 100, stdin) == NULL)
94                 password[0] = '\0';
95 #ifdef HAVE_TERMIOS_H
96         tcsetattr(0, TCSADRAIN, &t_orig);
97 #endif
98
99         length = strlen(password);
100         if (length > 0 && password[length - 1] != '\n')
101         {
102                 /* eat rest of the line */
103                 do
104                 {
105                         if (fgets(buf, sizeof(buf), stdin) == NULL)
106                                 break;
107                         buflen = strlen(buf);
108                 } while (buflen > 0 && buf[buflen - 1] != '\n');
109         }
110         if (length > 0 && password[length - 1] == '\n')
111                 password[length - 1] = '\0';
112
113         fprintf(stderr, "\n\n");
114 }
115
116
117 static void
118 _check_database_version(ArchiveHandle *AH, bool ignoreVersion)
119 {
120         PGresult   *res;
121         double      myversion;
122         const char *remoteversion_str;
123         double      remoteversion;
124         PGconn          *conn = AH->connection;
125
126         myversion = strtod(PG_VERSION, NULL);
127         res = PQexec(conn, "SELECT version()");
128         if (!res ||
129                 PQresultStatus(res) != PGRES_TUPLES_OK ||
130                 PQntuples(res) != 1)
131
132                 die_horribly(AH, "check_database_version(): command failed.  "
133                                 "Explanation from backend: '%s'.\n", PQerrorMessage(conn));
134
135         remoteversion_str = PQgetvalue(res, 0, 0);
136         remoteversion = strtod(remoteversion_str + 11, NULL);
137         if (myversion != remoteversion)
138         {
139                 fprintf(stderr, "Database version: %s\n%s version: %s\n",
140                                 remoteversion_str, progname, PG_VERSION);
141                 if (ignoreVersion)
142                         fprintf(stderr, "Proceeding despite version mismatch.\n");
143                 else
144                         die_horribly(AH, "Aborting because of version mismatch.\n"
145                                     "Use --ignore-version if you think it's safe to proceed anyway.\n");        
146         }
147         PQclear(res);
148 }
149
150 /* 
151  * Check if a given user is a superuser.
152  */
153 int UserIsSuperuser(ArchiveHandle *AH, char* user)
154 {
155         PQExpBuffer                     qry = createPQExpBuffer();
156         PGresult                        *res;
157         int                                     i_usesuper;
158         int                                     ntups;
159         int                                     isSuper;
160
161         /* Get the superuser setting */
162         appendPQExpBuffer(qry, "select usesuper from pg_user where usename = '%s'", user);
163         res = PQexec(AH->connection, qry->data);
164
165         if (!res)
166                 die_horribly(AH, "%s: null result checking superuser status of %s.\n",
167                                         progname, user);
168
169         if (PQresultStatus(res) != PGRES_TUPLES_OK)
170                 die_horribly(AH, "%s: Could not check superuser status of %s. Explanation from backend: %s\n",
171                                         progname, user, PQerrorMessage(AH->connection));
172
173         ntups = PQntuples(res);
174
175         if (ntups == 0)
176                 isSuper = 0;
177         else
178         {
179                 i_usesuper = PQfnumber(res, "usesuper");
180                 isSuper = (strcmp(PQgetvalue(res, 0, i_usesuper), "t") == 0);
181         }
182         PQclear(res);
183
184         return isSuper;
185 }
186
187 int ConnectedUserIsSuperuser(ArchiveHandle *AH)
188 {
189         return UserIsSuperuser(AH, PQuser(AH->connection));
190 }
191
192 char* ConnectedUser(ArchiveHandle *AH)
193 {
194         return PQuser(AH->connection);
195 }
196
197 /*
198  * Reconnect the DB associated with the archive handle 
199  */
200 int ReconnectDatabase(ArchiveHandle *AH, const char* newdbname, char *newUser)
201 {
202         PGconn          *newConn;
203         char            *dbname;
204
205         if (!newdbname || (strcmp(newdbname, "-") == 0) )
206                 dbname = PQdb(AH->connection);
207         else
208                 dbname = (char*)newdbname;
209
210         /* Let's see if the request is already satisfied */
211         if (strcmp(PQuser(AH->connection), newUser) == 0 && strcmp(newdbname, PQdb(AH->connection)) == 0)
212                 return 1;
213
214         newConn = _connectDB(AH, dbname, newUser);
215
216         PQfinish(AH->connection);
217         AH->connection = newConn;
218         strcpy(AH->username, newUser);
219
220         return 1;
221 }
222
223 /*
224  * Connect to the db again.
225  */
226 static PGconn* _connectDB(ArchiveHandle *AH, const char* reqdb, char *requser)
227 {
228         int                     need_pass;
229         PGconn          *newConn;
230         char            password[100];
231         char            *pwparam = NULL;
232         int                     badPwd = 0;
233         int                     noPwd = 0;
234         char            *newdb;
235         char            *newuser;
236
237         if (!reqdb || (strcmp(reqdb, "-") == 0) )
238                 newdb = PQdb(AH->connection);
239         else
240                 newdb = (char*)reqdb;
241
242         if (!requser || (strlen(requser) == 0))
243                 newuser = PQuser(AH->connection);
244         else
245                 newuser = (char*)requser;
246
247         ahlog(AH, 1, "Connecting to %s as %s\n", newdb, newuser);
248
249         do
250         {
251                 need_pass = false;
252                 newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
253                                                                 NULL, NULL, newdb, 
254                                                                 newuser, pwparam);
255                 if (!newConn)
256                         die_horribly(AH, "%s: Failed to reconnect (PQsetdbLogin failed).\n", progname);
257
258                 if (PQstatus(newConn) == CONNECTION_BAD)
259                 {
260                         noPwd = (strcmp(PQerrorMessage(newConn), "fe_sendauth: no password supplied\n") == 0);
261                         badPwd = (strncmp(PQerrorMessage(newConn), "Password authentication failed for user", 39)
262                                                 == 0);
263
264                         if (noPwd || badPwd) 
265                         {
266
267                                 if (badPwd)
268                                         fprintf(stderr, "Password incorrect\n");
269
270                                 fprintf(stderr, "Connecting to %s as %s\n", PQdb(AH->connection), newuser);
271
272                                 need_pass = true;
273                                 _prompt_for_password(newuser, password);
274                                 pwparam = password; 
275                         }
276                         else
277                                 die_horribly(AH, "%s: Could not reconnect. %s\n", progname, PQerrorMessage(newConn));
278                 }
279
280         } while (need_pass);
281
282         return newConn;
283 }
284
285
286 PGconn* ConnectDatabase(Archive *AHX, 
287                 const char*     dbname,
288                 const char*     pghost,
289                 const char*     pgport,
290                 const int               reqPwd,
291                 const int               ignoreVersion)
292 {
293         ArchiveHandle   *AH = (ArchiveHandle*)AHX;
294         char                    connect_string[512] = "";
295         char                    tmp_string[128];
296         char                    password[100];
297
298         if (AH->connection)
299                 die_horribly(AH, "%s: already connected to database\n", progname);
300
301         if (!dbname && !(dbname = getenv("PGDATABASE")) ) 
302                 die_horribly(AH, "%s: no database name specified\n", progname);
303
304         AH->dbname = strdup(dbname);
305
306         if (pghost != NULL)
307         {
308                 AH->pghost = strdup(pghost);
309                 sprintf(tmp_string, "host=%s ", AH->pghost);
310                 strcat(connect_string, tmp_string);
311         }
312         else
313             AH->pghost = NULL;
314
315         if (pgport != NULL)
316         {
317                 AH->pgport = strdup(pgport);
318                 sprintf(tmp_string, "port=%s ", AH->pgport);
319                 strcat(connect_string, tmp_string);
320         }
321         else
322             AH->pgport = NULL;
323
324         sprintf(tmp_string, "dbname=%s ", AH->dbname);
325         strcat(connect_string, tmp_string);
326
327         if (reqPwd)
328         {
329                 AH->username[0] = '\0';
330                 _prompt_for_password(AH->username, password);
331                 strcat(connect_string, "authtype=password ");
332                 sprintf(tmp_string, "user=%s ", AH->username);
333                 strcat(connect_string, tmp_string);
334                 sprintf(tmp_string, "password=%s ", password);
335                 strcat(connect_string, tmp_string);
336                 MemSet(tmp_string, 0, sizeof(tmp_string));
337                 MemSet(password, 0, sizeof(password));
338         }
339         AH->connection = PQconnectdb(connect_string);
340         MemSet(connect_string, 0, sizeof(connect_string));
341
342         /* check to see that the backend connection was successfully made */
343         if (PQstatus(AH->connection) == CONNECTION_BAD)
344                 die_horribly(AH, "Connection to database '%s' failed.\n%s\n",
345                                                 AH->dbname, PQerrorMessage(AH->connection));
346
347         /* check for version mismatch */
348         _check_database_version(AH, ignoreVersion);
349
350         /*
351      * AH->currUser = PQuser(AH->connection);
352          *      
353          * Removed because it prevented an initial \connect
354          * when dumping to SQL in pg_dump.
355          */
356
357         return AH->connection;
358 }
359
360 /* Public interface */
361 /* Convenience function to send a query. Monitors result to handle COPY statements */
362 int ExecuteSqlCommand(ArchiveHandle* AH, PQExpBuffer qry, char *desc)
363 {
364         return _executeSqlCommand(AH, AH->connection, qry, desc);
365 }
366
367 /* 
368  * Handle command execution. This is used to execute a command on more than one connection,
369  * but the 'pgCopyIn' setting assumes the COPY commands are ONLY executed on the primary
370  * setting...an error will be raised otherwise.
371  */
372 static int _executeSqlCommand(ArchiveHandle* AH, PGconn *conn, PQExpBuffer qry, char *desc)
373 {
374         PGresult                *res;
375
376         /* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */
377         res = PQexec(conn, qry->data);
378         if (!res)
379                 die_horribly(AH, "%s: %s. No result from backend.\n", progname, desc);
380
381     if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
382         {
383                 if (PQresultStatus(res) == PGRES_COPY_IN)
384                 {
385                         if (conn != AH->connection)
386                                 die_horribly(AH, "%s: COPY command execute in non-primary connection.\n", progname);
387
388                         AH->pgCopyIn = 1;
389                 }
390                 else 
391                         die_horribly(AH, "%s: %s. Code = %d. Explanation from backend: '%s'.\n",
392                                                 progname, desc, PQresultStatus(res), PQerrorMessage(AH->connection));
393         }
394
395         PQclear(res);
396
397         return strlen(qry->data);
398 }
399
400 /* Convenience function to send one or more queries. Monitors result to handle COPY statements */
401 int ExecuteSqlCommandBuf(ArchiveHandle* AH, void *qryv, int bufLen)
402 {
403         int                             loc;
404         int                             pos = 0;
405         int                             sPos = 0;
406         char                    *qry = (char*)qryv;
407         int                             isEnd = 0;
408         char                    *eos = qry + bufLen;
409
410         /* fprintf(stderr, "\n\n*****\n Buffer:\n\n%s\n*******************\n\n", qry); */
411
412         /* If we're in COPY IN mode, then just break it into lines and send... */
413         if (AH->pgCopyIn) {
414                 for(;;) {
415
416                         /* Find a lf */
417                         loc = strcspn(&qry[pos], "\n") + pos;
418                         pos = 0;
419
420                         /* If no match, then wait */
421                         if (loc >= (eos - qry)) /* None found */
422                         {
423                                 appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
424                                 break;
425                         };
426
427                     /* fprintf(stderr, "Found cr at %d, prev char was %c, next was %c\n", loc, qry[loc-1], qry[loc+1]); */
428         
429                         /* Count the number of preceding slashes */
430                         sPos = loc;
431                         while (sPos > 0 && qry[sPos-1] == '\\')
432                                 sPos--;
433
434                         sPos = loc - sPos;
435
436                         /* If an odd number of preceding slashes, then \n was escaped 
437                          * so set the next search pos, and restart (if any left).
438                          */
439                         if ((sPos & 1) == 1)
440                         {
441                                 /* fprintf(stderr, "cr was escaped\n"); */
442                                 pos = loc + 1;
443                                 if (pos >= (eos - qry))
444                                 {
445                                         appendBinaryPQExpBuffer(AH->pgCopyBuf, qry, (eos - qry));
446                                         break;
447                                 }
448                         }
449                         else
450                         {
451                                 /* We got a good cr */
452                                 qry[loc] = '\0';
453                                 appendPQExpBuffer(AH->pgCopyBuf, "%s\n", qry);
454                                 qry += loc + 1; 
455                                 isEnd = (strcmp(AH->pgCopyBuf->data, "\\.\n") == 0);
456
457                                 /* fprintf(stderr, "Sending '%s' via COPY (at end = %d)\n\n", AH->pgCopyBuf->data, isEnd); */ 
458                                 
459                                 if (PQputline(AH->connection, AH->pgCopyBuf->data) != 0)
460                                         die_horribly(AH, "%s: error returned by PQputline\n", progname);
461
462                                 resetPQExpBuffer(AH->pgCopyBuf);
463
464                                 /* fprintf(stderr, "Buffer is '%s'\n", AH->pgCopyBuf->data); */
465
466                                 if(isEnd) {
467                                         if (PQendcopy(AH->connection) != 0)
468                                                 die_horribly(AH, "%s: error returned by PQendcopy\n", progname);
469
470                                         AH->pgCopyIn = 0;
471                                         break;
472                                 }
473
474                         }
475
476                         /* Make sure we're not past the original buffer end */
477                         if (qry >= eos)
478                                 break;
479
480                 }
481         }
482
483         /* We may have finished Copy In, and have a non-empty buffer */
484         if (!AH->pgCopyIn) {
485
486                 /* 
487                  * The following is a mini state machine to assess then of of an SQL statement.
488                  * It really only needs to parse good SQL, or at least that's the theory...
489                  * End-of-statement is assumed to be an unquoted, un commented semi-colon.
490                  */
491
492                 /* fprintf(stderr, "Buffer at start is: '%s'\n\n", AH->sqlBuf->data); */
493
494                 for(pos=0; pos < (eos - qry); pos++)
495                 {
496                         appendPQExpBufferChar(AH->sqlBuf, qry[pos]);
497                         /* fprintf(stderr, " %c",qry[pos]); */
498
499                         switch (AH->sqlparse.state) {
500
501                                 case SQL_SCAN: /* Default state == 0, set in _allocAH */
502
503                                         if (qry[pos] == ';' && AH->sqlparse.braceDepth == 0)
504                                         {
505                                                 /* Send It & reset the buffer */
506                                                 /* fprintf(stderr, "    sending: '%s'\n\n", AH->sqlBuf->data); */
507                                                 ExecuteSqlCommand(AH, AH->sqlBuf, "Could not execute query");
508                                                 resetPQExpBuffer(AH->sqlBuf);
509                                                 AH->sqlparse.lastChar = '\0';
510                                         } 
511                                         else 
512                                         {
513                                                 if (qry[pos] == '"' || qry[pos] == '\'')
514                                                 {       
515                                                         /* fprintf(stderr,"[startquote]\n"); */
516                                                         AH->sqlparse.state = SQL_IN_QUOTE;
517                                                         AH->sqlparse.quoteChar = qry[pos];
518                                                         AH->sqlparse.backSlash = 0;
519                                                 } 
520                                                 else if (qry[pos] == '-' && AH->sqlparse.lastChar == '-')
521                                                 {
522                                                         AH->sqlparse.state = SQL_IN_SQL_COMMENT;
523                                                 } 
524                                                 else if (qry[pos] == '*' && AH->sqlparse.lastChar == '/')
525                                                 {
526                                                         AH->sqlparse.state = SQL_IN_EXT_COMMENT;
527                                                 } 
528                                                 else if ( qry[pos] == '(' )
529                                                 {
530                                                         AH->sqlparse.braceDepth++;
531                                                 }
532                                                 else if (qry[pos] == ')')
533                                                 {
534                                                         AH->sqlparse.braceDepth--;
535                                                 }
536
537                                                 AH->sqlparse.lastChar = qry[pos];
538                                         }
539
540                                         break;
541
542                                 case SQL_IN_SQL_COMMENT:
543
544                                         if (qry[pos] == '\n')
545                                                 AH->sqlparse.state = SQL_SCAN;
546                                         break;
547
548                                 case SQL_IN_EXT_COMMENT:
549
550                                         if (AH->sqlparse.lastChar == '*' && qry[pos] == '/')
551                                                 AH->sqlparse.state = SQL_SCAN;
552                                         break;
553
554                                 case SQL_IN_QUOTE:
555
556                                         if (!AH->sqlparse.backSlash && AH->sqlparse.quoteChar == qry[pos])
557                                         {
558                                                 /* fprintf(stderr,"[endquote]\n"); */
559                                                 AH->sqlparse.state = SQL_SCAN;
560                                         } 
561                                         else 
562                                         {
563
564                                                 if (qry[pos] == '\\')
565                                                 {
566                                                         if (AH->sqlparse.lastChar == '\\')
567                                                                 AH->sqlparse.backSlash = !AH->sqlparse.backSlash;
568                                                         else
569                                                                 AH->sqlparse.backSlash = 1;
570                                                         } else {
571                                                                 AH->sqlparse.backSlash = 0;
572                                                 }
573                                         }
574                                         break;
575
576                         }
577                         AH->sqlparse.lastChar = qry[pos];
578                         /* fprintf(stderr, "\n"); */
579                 }
580
581         }
582
583         return 1;
584 }
585
586 void FixupBlobRefs(ArchiveHandle *AH, char *tablename)
587 {
588         PQExpBuffer             tblQry = createPQExpBuffer();
589         PGresult                *res, *uRes;
590         int                             i, n;
591         char                    *attr;
592
593         if (strcmp(tablename, BLOB_XREF_TABLE) == 0)
594                 return;
595
596         appendPQExpBuffer(tblQry, "SELECT a.attname FROM pg_class c, pg_attribute a, pg_type t "
597                                                                 " WHERE a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid "
598                                                                 " AND t.typname = 'oid' AND c.relname = '%s';", tablename);
599
600         res = PQexec(AH->blobConnection, tblQry->data);
601         if (!res)
602                 die_horribly(AH, "%s: could not find OID attrs of %s. Explanation from backend '%s'\n",
603                                                 progname, tablename, PQerrorMessage(AH->connection));
604
605         if ((n = PQntuples(res)) == 0) {
606                 /* We're done */
607                 ahlog(AH, 1, "No OID attributes in table %s\n", tablename);
608                 PQclear(res);
609                 return;
610         }
611
612         for (i = 0 ; i < n ; i++)
613         {
614                 attr = PQgetvalue(res, i, 0);
615
616                 ahlog(AH, 1, " - %s.%s\n", tablename, attr);
617
618                 resetPQExpBuffer(tblQry);
619
620                 /*
621                  * We should use coalesce here (rather than 'exists'), but it seems to 
622                  * be broken in 7.0.2 (weird optimizer strategy)
623                  */
624                 appendPQExpBuffer(tblQry, "UPDATE \"%s\" SET \"%s\" = ",tablename, attr);
625                 appendPQExpBuffer(tblQry, " (SELECT x.newOid FROM \"%s\" x WHERE x.oldOid = \"%s\".\"%s\")",
626                                                                         BLOB_XREF_TABLE, tablename, attr);
627                 appendPQExpBuffer(tblQry, " where exists"
628                                                                         "(select * from %s x where x.oldOid = \"%s\".\"%s\");",
629                                                                         BLOB_XREF_TABLE, tablename, attr);
630
631                 ahlog(AH, 10, " - sql:\n%s\n", tblQry->data);
632
633                 uRes = PQexec(AH->blobConnection, tblQry->data);
634                 if (!uRes)
635                         die_horribly(AH, "%s: could not update attr %s of table %s. Explanation from backend '%s'\n",
636                                                                 progname, attr, tablename, PQerrorMessage(AH->blobConnection));
637
638                 if ( PQresultStatus(uRes) != PGRES_COMMAND_OK )
639                         die_horribly(AH, "%s: error while updating attr %s of table %s (result = %d)."
640                                                                 " Explanation from backend '%s'\n",
641                                                                 progname, attr, tablename, PQresultStatus(uRes), 
642                                                                 PQerrorMessage(AH->blobConnection));
643
644                 PQclear(uRes);
645         }
646
647         PQclear(res);
648
649 }
650
651 /**********
652  *      Convenient SQL calls
653  **********/
654 void CreateBlobXrefTable(ArchiveHandle* AH)
655 {
656         PQExpBuffer             qry = createPQExpBuffer();
657
658         /* IF we don't have a BLOB connection, then create one */
659         if (!AH->blobConnection)
660         {
661                 AH->blobConnection = _connectDB(AH, NULL, NULL);
662         }
663
664         ahlog(AH, 1, "Creating table for BLOBS xrefs\n");
665
666         appendPQExpBuffer(qry, "Create Temporary Table %s(oldOid oid, newOid oid);", BLOB_XREF_TABLE);
667
668         _executeSqlCommand(AH, AH->blobConnection, qry, "can not create BLOB xref table '" BLOB_XREF_TABLE "'");
669
670         resetPQExpBuffer(qry);
671
672         appendPQExpBuffer(qry, "Create Unique Index %s_ix on %s(oldOid)", BLOB_XREF_TABLE, BLOB_XREF_TABLE);
673         _executeSqlCommand(AH, AH->blobConnection, qry, "can not create index on BLOB xref table '" BLOB_XREF_TABLE "'");
674 }
675
676 void InsertBlobXref(ArchiveHandle* AH, int old, int new)
677 {
678         PQExpBuffer     qry = createPQExpBuffer();
679
680         appendPQExpBuffer(qry, "Insert Into %s(oldOid, newOid) Values (%d, %d);", BLOB_XREF_TABLE, old, new);
681
682         _executeSqlCommand(AH, AH->blobConnection, qry, "can not create BLOB xref entry");
683 }
684
685 void StartTransaction(ArchiveHandle* AH)
686 {
687         PQExpBuffer             qry = createPQExpBuffer();
688
689         appendPQExpBuffer(qry, "Begin;");
690
691         ExecuteSqlCommand(AH, qry, "can not start database transaction");
692         AH->txActive = true;
693 }
694
695 void StartTransactionXref(ArchiveHandle* AH)
696 {
697         PQExpBuffer             qry = createPQExpBuffer();
698
699         appendPQExpBuffer(qry, "Begin;");
700
701         _executeSqlCommand(AH, AH->blobConnection, qry, "can not start BLOB xref transaction");
702         AH->blobTxActive = true;
703 }
704
705 void CommitTransaction(ArchiveHandle* AH)
706 {
707     PQExpBuffer     qry = createPQExpBuffer();
708
709     appendPQExpBuffer(qry, "Commit;");
710
711     ExecuteSqlCommand(AH, qry, "can not commit database transaction");
712         AH->txActive = false;
713 }
714
715 void CommitTransactionXref(ArchiveHandle* AH)
716 {
717         PQExpBuffer             qry = createPQExpBuffer();
718
719         appendPQExpBuffer(qry, "Commit;");
720
721         _executeSqlCommand(AH, AH->blobConnection, qry, "can not commit BLOB xref transaction");
722         AH->blobTxActive = false;
723 }