public inbox for [email protected]help / color / mirror / Atom feed
[PATCH] pg_dump: use a cursor in getBlobs.. 27+ messages / 4 participants [nested] [flat]
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Justin Pryzby @ 2021-03-09 20:06 UTC (permalink / raw) ..to mitigate huge memory use in the case of millions of large objects --- src/bin/pg_dump/pg_dump.c | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index aa02ada079..3fd7f48605 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3333,7 +3333,7 @@ getBlobs(Archive *fout) BlobInfo *binfo; DumpableObject *bdata; PGresult *res; - int ntups; + int ntups, total = 0; int i; int i_oid; int i_lomowner; @@ -3341,9 +3341,12 @@ getBlobs(Archive *fout) int i_rlomacl; int i_initlomacl; int i_initrlomacl; + const char *blobFetchQry = "FETCH 1000 IN blob"; pg_log_info("reading large objects"); + appendPQExpBuffer(blobQry, "DECLARE blob CURSOR FOR "); + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (fout->remoteVersion >= 90600) { @@ -3393,58 +3396,66 @@ getBlobs(Archive *fout) "NULL::oid AS initrlomacl " " FROM pg_largeobject"); - res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); + ExecuteSqlStatement(fout, blobQry->data); + destroyPQExpBuffer(blobQry); - i_oid = PQfnumber(res, "oid"); - i_lomowner = PQfnumber(res, "rolname"); - i_lomacl = PQfnumber(res, "lomacl"); - i_rlomacl = PQfnumber(res, "rlomacl"); - i_initlomacl = PQfnumber(res, "initlomacl"); - i_initrlomacl = PQfnumber(res, "initrlomacl"); + do { + res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK); - ntups = PQntuples(res); + i_oid = PQfnumber(res, "oid"); + i_lomowner = PQfnumber(res, "rolname"); + i_lomacl = PQfnumber(res, "lomacl"); + i_rlomacl = PQfnumber(res, "rlomacl"); + i_initlomacl = PQfnumber(res, "initlomacl"); + i_initrlomacl = PQfnumber(res, "initrlomacl"); - /* - * Each large object has its own BLOB archive entry. - */ - binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); + ntups = PQntuples(res); + total += ntups; - for (i = 0; i < ntups; i++) - { - binfo[i].dobj.objType = DO_BLOB; - binfo[i].dobj.catId.tableoid = LargeObjectRelationId; - binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); - AssignDumpId(&binfo[i].dobj); + /* + * Each large object has its own BLOB archive entry. + */ + binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo)); - binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); - binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); - binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); - binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); - binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); - binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + for (i = 0; i < ntups; i++) + { + binfo[i].dobj.objType = DO_BLOB; + binfo[i].dobj.catId.tableoid = LargeObjectRelationId; + binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&binfo[i].dobj); + + binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid)); + binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner)); + binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl)); + binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl)); + binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl)); + binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl)); + + if (PQgetisnull(res, i, i_lomacl) && + PQgetisnull(res, i, i_rlomacl) && + PQgetisnull(res, i, i_initlomacl) && + PQgetisnull(res, i, i_initrlomacl)) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; - if (PQgetisnull(res, i, i_lomacl) && - PQgetisnull(res, i, i_rlomacl) && - PQgetisnull(res, i, i_initlomacl) && - PQgetisnull(res, i, i_initrlomacl)) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL; + /* + * In binary-upgrade mode for blobs, we do *not* dump out the blob + * data, as it will be copied by pg_upgrade, which simply copies the + * pg_largeobject table. We *do* however dump out anything but the + * data, as pg_upgrade copies just pg_largeobject, but not + * pg_largeobject_metadata, after the dump is restored. + */ + if (dopt->binary_upgrade) + binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; + } - /* - * In binary-upgrade mode for blobs, we do *not* dump out the blob - * data, as it will be copied by pg_upgrade, which simply copies the - * pg_largeobject table. We *do* however dump out anything but the - * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. - */ - if (dopt->binary_upgrade) - binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA; - } + PQclear(res); + } while (ntups != 0); /* * If we have any large objects, a "BLOBS" archive entry is needed. This * is just a placeholder for sorting; it carries no data now. */ - if (ntups > 0) + if (total > 0) { bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject)); bdata->objType = DO_BLOB_DATA; @@ -3452,9 +3463,6 @@ getBlobs(Archive *fout) AssignDumpId(bdata); bdata->name = pg_strdup("BLOBS"); } - - PQclear(res); - destroyPQExpBuffer(blobQry); } /* -- 2.17.0 --gatW/ieO32f1wygP-- ^ permalink raw reply [nested|flat] 27+ messages in thread
* Re: Bug in detaching a partition with a foreign key. @ 2025-01-20 06:41 Amul Sul <[email protected]> 0 siblings, 1 reply; 27+ messages in thread From: Amul Sul @ 2025-01-20 06:41 UTC (permalink / raw) To: Sami Imseih <[email protected]>; +Cc: pgsql-hackers On Sat, Jan 18, 2025 at 2:14 AM Sami Imseih <[email protected]> wrote: > > This is a bug indeed. I tried your patch, but it ends up in a seg fault. > > [...] > If the relation on the parent and child constraint match, that > tells us we don't have inheritance. > So, I am thinking we should add another condition for checking > if a foreign key is inherited by checking if the parent constraint > relation is different from the child constraint relation. > > I am attaching an unpolished patch ( we need test coverage as well ) that > implements the above. All tests pass with this patch. Thanks for confirming the bug. TBH, I haven't had a chance to look into the issue in detail yet, but my understanding of the detach operation is that it should cut the link between the parent constraint and the constraint on the partition being detached. The trial patch I posted was meant to prevent cutting the link between the partition's constraint and the constraint that inherits it. Your patch seems to achieve the same, AFAIUC, but I'm not sure I like the way it's doing, nor the trial patch I posted. A crash is certainly possible with that patch, but it would be helpful if you could provide more context -- such as crash detail and/or a test case. Regards, Amul ^ permalink raw reply [nested|flat] 27+ messages in thread
* Re: Bug in detaching a partition with a foreign key. @ 2025-01-20 15:15 Sami Imseih <[email protected]> parent: Amul Sul <[email protected]> 0 siblings, 1 reply; 27+ messages in thread From: Sami Imseih @ 2025-01-20 15:15 UTC (permalink / raw) To: Amul Sul <[email protected]>; +Cc: pgsql-hackers > should cut the link between the parent constraint and the constraint on > the partition being detached. correct by setting the conparentid to 0 in pg_constraint and to delete the pg_depend record for partition dependency. But in the repro case, we don't have a dependency as the table the foreign key is on is a partition. > The trial patch I posted was meant to prevent > cutting the link between the partition's constraint and the constraint that > inherits it. Your patch seems to achieve the same, AFAIUC, Yes, exactly. > but it would be helpful if you could provide more > context -- such as crash detail and/or a test case. Below is the repro I used. Similar as you original repro, but without subpartition on foo_p0. This also results in the segfault with your attached patch. "" CREATE TABLE bar(id int PRIMARY KEY) PARTITION BY RANGE(id); CREATE TABLE bar_p0 PARTITION OF bar FOR VALUES FROM (0) TO (100); CREATE TABLE foo(id int) PARTITION BY RANGE(id); CREATE TABLE foo_p0 PARTITION OF foo FOR VALUES FROM (0) TO (100); ALTER TABLE foo_p0 ADD CONSTRAINT child_fk_con FOREIGN KEY (id) REFERENCES bar; ALTER TABLE foo DETACH PARTITION foo_p0; """ Here is the core dump """ Using host libthread_db library "/lib64/libthread_db.so.1". Core was generated by `postgres: postgres postgres [local] ALTER T'. Program terminated with signal SIGSEGV, Segmentation fault. #0 GetMemoryChunkMethodID (pointer=0x0) at mcxt.c:205 205 header = *((const uint64 *) ((const char *) pointer - sizeof(uint64))); Thread 1 (Thread 0xffffb6e34f00 (LWP 17508)): #0 GetMemoryChunkMethodID (pointer=0x0) at mcxt.c:205 header = 12273896 #1 0x0000000000c05edc in repalloc (pointer=0x0, size=128) at mcxt.c:1566 ret = 0x10b5b98380 #2 0x00000000007fbf24 in enlarge_list (list=0x18cd1490, min_size=1) at list.c:209 new_max_len = 16 #3 0x00000000007fc22c in new_tail_cell (list=0x18cd1490) at list.c:327 No locals. #4 0x00000000007fc334 in lappend_oid (list=0x18cd1490, datum=16404) at list.c:382 No locals. """ I was able to get your patch working by changing the way the fksid is built with the below: /* Collect all the constraint ids */ foreach(cell, fks) { ForeignKeyCacheInfo *fk = lfirst(cell); fksids = lappend_oid(fksids, fk->conoid); } Regards, Sami ^ permalink raw reply [nested|flat] 27+ messages in thread
* Re: Bug in detaching a partition with a foreign key. @ 2025-01-20 15:26 Álvaro Herrera <[email protected]> parent: Sami Imseih <[email protected]> 0 siblings, 0 replies; 27+ messages in thread From: Álvaro Herrera @ 2025-01-20 15:26 UTC (permalink / raw) To: Sami Imseih <[email protected]>; +Cc: Amul Sul <[email protected]>; pgsql-hackers On 2025-Jan-20, Sami Imseih wrote: > Below is the repro I used. Similar as you original repro, > but without subpartition on foo_p0. This also results in the segfault > with your attached patch. I think the issue in Amul's patch is just that the list was not initialized to NIL. Other than the lack of comments, which I'm in the process of writing, the initial patch Amul submitted seems largely correct to me, in my understanding of the situation. I'll throw in a few tests, hoping that it'll prove correct, and if so then I'll get it pushed soon. -- Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/ "El hombre nunca sabe de lo que es capaz hasta que lo intenta" (C. Dickens) ^ permalink raw reply [nested|flat] 27+ messages in thread
end of thread, other threads:[~2025-01-20 15:26 UTC | newest] Thread overview: 27+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2021-03-09 20:06 [PATCH] pg_dump: use a cursor in getBlobs.. Justin Pryzby <[email protected]> 2025-01-20 06:41 Re: Bug in detaching a partition with a foreign key. Amul Sul <[email protected]> 2025-01-20 15:15 ` Re: Bug in detaching a partition with a foreign key. Sami Imseih <[email protected]> 2025-01-20 15:26 ` Re: Bug in detaching a partition with a foreign key. Álvaro Herrera <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox