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