public inbox for [email protected]help / color / mirror / Atom feed
[PATCH] pg_dump: use a cursor in getBlobs.. 25+ messages / 2 participants [nested] [flat]
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* [PATCH] pg_dump: use a cursor in getBlobs.. @ 2021-03-09 20:06 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 25+ 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] 25+ messages in thread
* Re: Inconsistent RestrictInfo serial numbers @ 2024-10-11 06:02 Ashutosh Bapat <[email protected]> 0 siblings, 0 replies; 25+ messages in thread From: Ashutosh Bapat @ 2024-10-11 06:02 UTC (permalink / raw) To: Richard Guo <[email protected]>; +Cc: pgsql-hackers On Thu, Oct 10, 2024 at 7:37 AM Richard Guo <[email protected]> wrote: > > > > > > So may > > > > be we should do this processing elsewhere and replace the original > > > > clause itself? > > > > > > I’m not sure about this. Different versions of the same qual clause > > > can lead to different conclusions about whether it can be reduced to > > > constant-FALSE. > > > > Can you please provide an example? > > I think the query I provided in my initial email serves as an example. > To quote what I said there: > > " > In the query above, the has_clone version of qual 't1.a is null' would > be reduced to constant-FALSE, while the is_clone version would not. > " > > > > I don't think it is possible to replace the original > > > clause; we need to do this processing on a version-by-version basis. > > > > > > > Either way, each version will be represented by only one RestrictInfo. > > Why can't we replace that version instead of creating a new > > RestrictInfo? > > Sure we can do that. However, I find that manually altering a > well-formed RestrictInfo's clause (along with its left_relids, > right_relids, clause_relids, num_base_rels, etc.) seems too > hacky to me. That's fair. 1. What strikes me as odd in your patch is that the last_rinfo_serial is reset so far away from the new clause that will be created with the next last_rinfo_serial OR the clause whose clones are being created. It either indicates another site where we are missing (possibly future) to restore rinfo_serial in a clone OR reset of last_serial_rinfo needs to happen somewhere else. Why isn't resetting last_rinfo_serial in deconstruct_distribute_oj_quals() enough? 2. This change would also prevent add_base_clause_to_rel() and add_join_clause_to_rels() from being used anywhere outside the context of deconstruct_distribute_oj_quals() since these functions would reset last_rinfo_serial when it's expected to be incremented. Moreover another make_restrictinfo() call somewhere in the minions of distribute_qual_to_rels() or distribute_quals_to_rels() would cause a similar bug. 3. Do we need to reset last_serial_rinfo everywhere we reset rinfo_serial e.g. create_join_clause()? I suspect that b262ad440edecda0b1aba81d967ab560a83acb8a didn't do anything wrong. It's effectively copying rinfo_serial of original clause into the derived clause. I would have liked it better if it would have used the same method as create_join_clause(). Some other commti failed to notice the some minion of distribute_quals_to_rels()->distribute_qual_to_rels() may increment last_rinfo_serial while creating an alternate RestrictInfo for the qual passed to distribute_qual_to_rels(). I think a robust fix is to reset last_rinfo_serial in distribute_quals_to_rels() for every qual and add an Assert(root->last_rinfo_serial == saved_last_rinfo_serial + list_length(quals)) before resetting last_rinfo_serial in deconstruct_distribute_oj_quals() to catch any future digressions. -- Best Wishes, Ashutosh Bapat ^ permalink raw reply [nested|flat] 25+ messages in thread
end of thread, other threads:[~2024-10-11 06:02 UTC | newest] Thread overview: 25+ 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]> 2024-10-11 06:02 Re: Inconsistent RestrictInfo serial numbers Ashutosh Bapat <[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