|
12 | 12 | * by PostgreSQL
|
13 | 13 | *
|
14 | 14 | * IDENTIFICATION
|
15 |
| - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.410 2005/06/21 20:45:44 tgl Exp $ |
| 15 | + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.411 2005/06/30 03:02:56 tgl Exp $ |
16 | 16 | *
|
17 | 17 | *-------------------------------------------------------------------------
|
18 | 18 | */
|
@@ -163,7 +163,9 @@ static void selectSourceSchema(const char *schemaName);
|
163 | 163 | static char *getFormattedTypeName(Oid oid, OidOptions opts);
|
164 | 164 | static char *myFormatType(const char *typname, int32 typmod);
|
165 | 165 | static const char *fmtQualifiedId(const char *schema, const char *id);
|
| 166 | +static bool hasBlobs(Archive *AH); |
166 | 167 | static int dumpBlobs(Archive *AH, void *arg);
|
| 168 | +static int dumpBlobComments(Archive *AH, void *arg); |
167 | 169 | static void dumpDatabase(Archive *AH);
|
168 | 170 | static void dumpEncoding(Archive *AH);
|
169 | 171 | static const char *getAttrName(int attrnum, TableInfo *tblInfo);
|
@@ -344,6 +346,7 @@ main(int argc, char **argv)
|
344 | 346 |
|
345 | 347 | case 's': /* dump schema only */
|
346 | 348 | schemaOnly = true;
|
| 349 | + outputBlobs = false; |
347 | 350 | break;
|
348 | 351 |
|
349 | 352 | case 'S': /* Username for superuser in plain text
|
@@ -539,16 +542,22 @@ main(int argc, char **argv)
|
539 | 542 | if (!schemaOnly)
|
540 | 543 | getTableData(tblinfo, numTables, oids);
|
541 | 544 |
|
542 |
| - if (outputBlobs) |
| 545 | + if (outputBlobs && hasBlobs(g_fout)) |
543 | 546 | {
|
544 |
| - /* This is just a placeholder to allow correct sorting of blobs */ |
| 547 | + /* Add placeholders to allow correct sorting of blobs */ |
545 | 548 | DumpableObject *blobobj;
|
546 | 549 |
|
547 | 550 | blobobj = (DumpableObject *) malloc(sizeof(DumpableObject));
|
548 | 551 | blobobj->objType = DO_BLOBS;
|
549 | 552 | blobobj->catId = nilCatalogId;
|
550 | 553 | AssignDumpId(blobobj);
|
551 | 554 | blobobj->name = strdup("BLOBS");
|
| 555 | + |
| 556 | + blobobj = (DumpableObject *) malloc(sizeof(DumpableObject)); |
| 557 | + blobobj->objType = DO_BLOB_COMMENTS; |
| 558 | + blobobj->catId = nilCatalogId; |
| 559 | + AssignDumpId(blobobj); |
| 560 | + blobobj->name = strdup("BLOB COMMENTS"); |
552 | 561 | }
|
553 | 562 |
|
554 | 563 | /*
|
@@ -1313,52 +1322,82 @@ dumpEncoding(Archive *AH)
|
1313 | 1322 | }
|
1314 | 1323 |
|
1315 | 1324 |
|
| 1325 | +/* |
| 1326 | + * hasBlobs: |
| 1327 | + * Test whether database contains any large objects |
| 1328 | + */ |
| 1329 | +static bool |
| 1330 | +hasBlobs(Archive *AH) |
| 1331 | +{ |
| 1332 | + bool result; |
| 1333 | + const char *blobQry; |
| 1334 | + PGresult *res; |
| 1335 | + |
| 1336 | + /* Make sure we are in proper schema */ |
| 1337 | + selectSourceSchema("pg_catalog"); |
| 1338 | + |
| 1339 | + /* Check for BLOB OIDs */ |
| 1340 | + if (AH->remoteVersion >= 70100) |
| 1341 | + blobQry = "SELECT loid FROM pg_largeobject LIMIT 1"; |
| 1342 | + else |
| 1343 | + blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1"; |
| 1344 | + |
| 1345 | + res = PQexec(g_conn, blobQry); |
| 1346 | + check_sql_result(res, g_conn, blobQry, PGRES_TUPLES_OK); |
| 1347 | + |
| 1348 | + result = PQntuples(res) > 0; |
| 1349 | + |
| 1350 | + PQclear(res); |
| 1351 | + |
| 1352 | + return result; |
| 1353 | +} |
| 1354 | + |
1316 | 1355 | /*
|
1317 | 1356 | * dumpBlobs:
|
1318 | 1357 | * dump all blobs
|
1319 |
| - * |
1320 | 1358 | */
|
1321 | 1359 | static int
|
1322 | 1360 | dumpBlobs(Archive *AH, void *arg)
|
1323 | 1361 | {
|
1324 |
| - PQExpBuffer oidQry = createPQExpBuffer(); |
1325 |
| - PQExpBuffer oidFetchQry = createPQExpBuffer(); |
| 1362 | + const char *blobQry; |
| 1363 | + const char *blobFetchQry; |
1326 | 1364 | PGresult *res;
|
1327 |
| - int i; |
1328 |
| - int loFd; |
1329 | 1365 | char buf[LOBBUFSIZE];
|
| 1366 | + int i; |
1330 | 1367 | int cnt;
|
1331 |
| - Oid blobOid; |
1332 | 1368 |
|
1333 | 1369 | if (g_verbose)
|
1334 | 1370 | write_msg(NULL, "saving large objects\n");
|
1335 | 1371 |
|
1336 | 1372 | /* Make sure we are in proper schema */
|
1337 | 1373 | selectSourceSchema("pg_catalog");
|
1338 | 1374 |
|
1339 |
| - /* Cursor to get all BLOB tables */ |
| 1375 | + /* Cursor to get all BLOB OIDs */ |
1340 | 1376 | if (AH->remoteVersion >= 70100)
|
1341 |
| - appendPQExpBuffer(oidQry, "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject"); |
| 1377 | + blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject"; |
1342 | 1378 | else
|
1343 |
| - appendPQExpBuffer(oidQry, "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'"); |
| 1379 | + blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'"; |
1344 | 1380 |
|
1345 |
| - res = PQexec(g_conn, oidQry->data); |
1346 |
| - check_sql_result(res, g_conn, oidQry->data, PGRES_COMMAND_OK); |
| 1381 | + res = PQexec(g_conn, blobQry); |
| 1382 | + check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK); |
1347 | 1383 |
|
1348 |
| - /* Fetch for cursor */ |
1349 |
| - appendPQExpBuffer(oidFetchQry, "FETCH 1000 IN bloboid"); |
| 1384 | + /* Command to fetch from cursor */ |
| 1385 | + blobFetchQry = "FETCH 1000 IN bloboid"; |
1350 | 1386 |
|
1351 | 1387 | do
|
1352 | 1388 | {
|
1353 | 1389 | PQclear(res);
|
1354 | 1390 |
|
1355 | 1391 | /* Do a fetch */
|
1356 |
| - res = PQexec(g_conn, oidFetchQry->data); |
1357 |
| - check_sql_result(res, g_conn, oidFetchQry->data, PGRES_TUPLES_OK); |
| 1392 | + res = PQexec(g_conn, blobFetchQry); |
| 1393 | + check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK); |
1358 | 1394 |
|
1359 | 1395 | /* Process the tuples, if any */
|
1360 | 1396 | for (i = 0; i < PQntuples(res); i++)
|
1361 | 1397 | {
|
| 1398 | + Oid blobOid; |
| 1399 | + int loFd; |
| 1400 | + |
1362 | 1401 | blobOid = atooid(PQgetvalue(res, i, 0));
|
1363 | 1402 | /* Open the BLOB */
|
1364 | 1403 | loFd = lo_open(g_conn, blobOid, INV_READ);
|
@@ -1393,8 +1432,81 @@ dumpBlobs(Archive *AH, void *arg)
|
1393 | 1432 |
|
1394 | 1433 | PQclear(res);
|
1395 | 1434 |
|
1396 |
| - destroyPQExpBuffer(oidQry); |
1397 |
| - destroyPQExpBuffer(oidFetchQry); |
| 1435 | + return 1; |
| 1436 | +} |
| 1437 | + |
| 1438 | +/* |
| 1439 | + * dumpBlobComments |
| 1440 | + * dump all blob comments |
| 1441 | + * |
| 1442 | + * Since we don't provide any way to be selective about dumping blobs, |
| 1443 | + * there's no need to be selective about their comments either. We put |
| 1444 | + * all the comments into one big TOC entry. |
| 1445 | + */ |
| 1446 | +static int |
| 1447 | +dumpBlobComments(Archive *AH, void *arg) |
| 1448 | +{ |
| 1449 | + const char *blobQry; |
| 1450 | + const char *blobFetchQry; |
| 1451 | + PQExpBuffer commentcmd = createPQExpBuffer(); |
| 1452 | + PGresult *res; |
| 1453 | + int i; |
| 1454 | + |
| 1455 | + if (g_verbose) |
| 1456 | + write_msg(NULL, "saving large object comments\n"); |
| 1457 | + |
| 1458 | + /* Make sure we are in proper schema */ |
| 1459 | + selectSourceSchema("pg_catalog"); |
| 1460 | + |
| 1461 | + /* Cursor to get all BLOB comments */ |
| 1462 | + if (AH->remoteVersion >= 70200) |
| 1463 | + blobQry = "DECLARE blobcmt CURSOR FOR SELECT DISTINCT loid, obj_description(loid, 'pg_largeobject') FROM pg_largeobject"; |
| 1464 | + else if (AH->remoteVersion >= 70100) |
| 1465 | + blobQry = "DECLARE blobcmt CURSOR FOR SELECT DISTINCT loid, obj_description(loid) FROM pg_largeobject"; |
| 1466 | + else |
| 1467 | + blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, (SELECT description FROM pg_description pd WHERE pd.objoid=pc.oid) FROM pg_class pc WHERE relkind = 'l'"; |
| 1468 | + |
| 1469 | + res = PQexec(g_conn, blobQry); |
| 1470 | + check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK); |
| 1471 | + |
| 1472 | + /* Command to fetch from cursor */ |
| 1473 | + blobFetchQry = "FETCH 100 IN blobcmt"; |
| 1474 | + |
| 1475 | + do |
| 1476 | + { |
| 1477 | + PQclear(res); |
| 1478 | + |
| 1479 | + /* Do a fetch */ |
| 1480 | + res = PQexec(g_conn, blobFetchQry); |
| 1481 | + check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK); |
| 1482 | + |
| 1483 | + /* Process the tuples, if any */ |
| 1484 | + for (i = 0; i < PQntuples(res); i++) |
| 1485 | + { |
| 1486 | + Oid blobOid; |
| 1487 | + char *comment; |
| 1488 | + |
| 1489 | + /* ignore blobs without comments */ |
| 1490 | + if (PQgetisnull(res, i, 1)) |
| 1491 | + continue; |
| 1492 | + |
| 1493 | + blobOid = atooid(PQgetvalue(res, i, 0)); |
| 1494 | + comment = PQgetvalue(res, i, 1); |
| 1495 | + |
| 1496 | + printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ", |
| 1497 | + blobOid); |
| 1498 | + appendStringLiteral(commentcmd, comment, false); |
| 1499 | + appendPQExpBuffer(commentcmd, ";\n"); |
| 1500 | + |
| 1501 | + archputs(commentcmd->data, AH); |
| 1502 | + } |
| 1503 | + } while (PQntuples(res) > 0); |
| 1504 | + |
| 1505 | + PQclear(res); |
| 1506 | + |
| 1507 | + archputs("\n", AH); |
| 1508 | + |
| 1509 | + destroyPQExpBuffer(commentcmd); |
1398 | 1510 |
|
1399 | 1511 | return 1;
|
1400 | 1512 | }
|
@@ -4356,6 +4468,13 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
|
4356 | 4468 | NULL, 0,
|
4357 | 4469 | dumpBlobs, NULL);
|
4358 | 4470 | break;
|
| 4471 | + case DO_BLOB_COMMENTS: |
| 4472 | + ArchiveEntry(fout, dobj->catId, dobj->dumpId, |
| 4473 | + dobj->name, NULL, NULL, "", |
| 4474 | + false, "BLOB COMMENTS", "", "", NULL, |
| 4475 | + NULL, 0, |
| 4476 | + dumpBlobComments, NULL); |
| 4477 | + break; |
4359 | 4478 | }
|
4360 | 4479 | }
|
4361 | 4480 |
|
|
0 commit comments