pg_dump / copy bugs with "big lines" ?
Hello hackers,
I've tried my luck on pgsql-bugs before, with no success, so I report these
problem here.
The documentation mentions the following limits for sizes:
Maximum Field Size 1 GB
Maximum Row Size 1.6 TB
However, it seems like rows bigger than 1GB can't be COPYed out:
ro=# create table test_text (c1 text, c2 text);
CREATE TABLE
ro=# insert into test_text (c1) VALUES (repeat('a', 536870912));
INSERT 0 1
ro=# update test_text set c2 = c1;
UPDATE 1
Then, trying to dump or copy that results in the following error:
ro=# COPY test_text TO '/tmp/test';
ERROR: out of memory
DÉTAIL : Cannot enlarge string buffer containing 536870913 bytes by 536870912
more bytes.
In fact, the same thing happens when using a simple SELECT:
ro=# select * from test_text ;
ERROR: out of memory
DÉTAIL : Cannot enlarge string buffer containing 536870922 bytes by 536870912
more bytes.
In the case of COPY, the server uses a StringInfo to output the row. The
problem is, a StringInfo is capped to MAX_ALLOC_SIZE (1GB - 1), but a row
should be able to hold much more than that.
So, is this a bug ? Or is there a caveat I would have missed in the
documentation ?
We also hit a second issue, this time related to bytea encoding.
This test case is a bit more complicated, since I had to use an external
(client) program to insert my data. It involves inserting a string that fit
into 1GB when encoded in escape format, but is larger than that in hex, and
another string which fits in 1GB using the hex format, but is larger than that
in escape:
from psycopg2 import connect
from io import BytesIO
conn = connect(dbname="ro")
cur = conn.cursor()
fullcontent = BytesIO()
# Write a binary string that weight less
# than 1 GB when escape encoded, but more than
# that if hex encoded
for i in range(200):
fullcontent.write(b"aaa" * 1000000)
fullcontent.seek(0)
cur.copy_from(fullcontent, "test_bytea")
fullcontent.seek(0)
fullcontent.truncate()
# Write another binary string that weight
# less than 1GB when hex encoded, but more than
# that if escape encoded
cur.execute("SET bytea_output = 'hex'")
fullcontent.write(b"\\\\x")
for i in range(300):
fullcontent.write(b"00" * 1000000)
fullcontent.seek(0)
cur.copy_from(fullcontent, "test_bytea")
cur.execute("COMMIT;")
cur.close()
I couldn't find an invocation of pg_dump which would allow me to dump both
lines:
ro@ronan_laptop /tmp % PGOPTIONS="-c bytea_output=escape" pg_dump -Fc >
/dev/null
pg_dump: Dumping the contents of table "test_bytea" failed: PQgetResult()
failed.
pg_dump: Error message from server: ERROR: invalid memory alloc request size
1200000001
pg_dump: The command was: COPY public.test_bytea (c1) TO stdout;
ro@ronan_laptop /tmp % PGOPTIONS="-c bytea_output=hex" pg_dump -Fc >
/dev/null
pg_dump: Dumping the contents of table "test_bytea" failed: PQgetResult()
failed.
pg_dump: Error message from server: ERROR: invalid memory alloc request size
1200000003
pg_dump: The command was: COPY public.test_bytea (c1) TO stdout;
Using a COPY with binary format works:
ro=# COPY test_bytea TO '/tmp/test' WITH BINARY;
There seems to be a third issue, with regards to escape encoding: the
backslash character is escaped, by adding another backslash. This means that a
field which size is less than 1GB using the escape sequence will not be able
to be output once the backslash are escaped.
For example, lets consider a string consisting of 300000000 '\' characters:
ro=# select length(c1) from test_bytea;
length
-----------
300000000
(1 ligne)
ro=# select length(encode(c1, 'escape')) from test_bytea ;
length
-----------
600000000
(1 ligne)
ro=# set bytea_output to escape;
SET
ro=# copy test_bytea to '/tmp/test.csv' ;
ERROR: out of memory
DÉTAIL : Cannot enlarge string buffer containing 1073741822 bytes by 1 more
bytes.
I think pg_dump should not error out on any data which was valid upon
insertion. It seems the fix would be non-trivial, since StringInfo structures
are relying on a limit of MaxAllocSize. Or am I missing something ?
Thank you.
--
Ronan Dunklau
http://dalibo.com - http://dalibo.org
On 3/30/15 5:46 AM, Ronan Dunklau wrote:
Hello hackers,
I've tried my luck on pgsql-bugs before, with no success, so I report these
problem here.The documentation mentions the following limits for sizes:
Maximum Field Size 1 GB
Maximum Row Size 1.6 TBHowever, it seems like rows bigger than 1GB can't be COPYed out:
ro=# create table test_text (c1 text, c2 text);
CREATE TABLE
ro=# insert into test_text (c1) VALUES (repeat('a', 536870912));
INSERT 0 1
ro=# update test_text set c2 = c1;
UPDATE 1Then, trying to dump or copy that results in the following error:
ro=# COPY test_text TO '/tmp/test';
ERROR: out of memory
D�TAIL : Cannot enlarge string buffer containing 536870913 bytes by 536870912
more bytes.In fact, the same thing happens when using a simple SELECT:
ro=# select * from test_text ;
ERROR: out of memory
D�TAIL : Cannot enlarge string buffer containing 536870922 bytes by 536870912
more bytes.In the case of COPY, the server uses a StringInfo to output the row. The
problem is, a StringInfo is capped to MAX_ALLOC_SIZE (1GB - 1), but a row
should be able to hold much more than that.
Yeah, shoving a whole row into one StringInfo is ultimately going to
limit a row to 1G, which is a far cry from what the docs claim. There's
also going to be problems with FE/BE communications, because things like
pq_sendbyte all use StringInfo as a buffer too. So while Postgres can
store a 1.6TB row, you're going to find a bunch of stuff that doesn't
work past around 1GB.
So, is this a bug ? Or is there a caveat I would have missed in the
documentation ?
I suppose that really depends on your point of view. The real question
is whether we think it's worth fixing, or a good idea to change the
behavior of StringInfo.
StringInfo uses int's to store length, so it could possibly be changed,
but then you'd just error out due to MaxAllocSize.
Now perhaps those could both be relaxed, but certainly not to the extent
that you can shove an entire 1.6TB row into an output buffer.
The other issue is that there's a LOT of places in code that blindly
copy detoasted data around, so while we technically support 1GB toasted
values you're probably going to be quite unhappy with performance. I'm
actually surprised you haven't already seen this with 500MB objects.
So long story short, I'm not sure how worthwhile it would be to try and
fix this. We probably should improve the docs though.
Have you looked at using large objects for what you're doing? (Note that
those have their own set of challenges and limitations.)
We also hit a second issue, this time related to bytea encoding.
There's probably several other places this type of thing could be a
problem. I'm thinking of conversions in particular.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Le lundi 30 mars 2015 18:45:41 Jim Nasby a écrit :
On 3/30/15 5:46 AM, Ronan Dunklau wrote:
Hello hackers,
I've tried my luck on pgsql-bugs before, with no success, so I report
these
problem here.The documentation mentions the following limits for sizes:
Maximum Field Size 1 GB
Maximum Row Size 1.6 TBHowever, it seems like rows bigger than 1GB can't be COPYed out:
ro=# create table test_text (c1 text, c2 text);
CREATE TABLE
ro=# insert into test_text (c1) VALUES (repeat('a', 536870912));
INSERT 0 1
ro=# update test_text set c2 = c1;
UPDATE 1Then, trying to dump or copy that results in the following error:
ro=# COPY test_text TO '/tmp/test';
ERROR: out of memory
DÉTAIL : Cannot enlarge string buffer containing 536870913 bytes by
536870912 more bytes.In fact, the same thing happens when using a simple SELECT:
ro=# select * from test_text ;
ERROR: out of memory
DÉTAIL : Cannot enlarge string buffer containing 536870922 bytes by
536870912 more bytes.In the case of COPY, the server uses a StringInfo to output the row. The
problem is, a StringInfo is capped to MAX_ALLOC_SIZE (1GB - 1), but a row
should be able to hold much more than that.Yeah, shoving a whole row into one StringInfo is ultimately going to
limit a row to 1G, which is a far cry from what the docs claim. There's
also going to be problems with FE/BE communications, because things like
pq_sendbyte all use StringInfo as a buffer too. So while Postgres can
store a 1.6TB row, you're going to find a bunch of stuff that doesn't
work past around 1GB.So, is this a bug ? Or is there a caveat I would have missed in the
documentation ?I suppose that really depends on your point of view. The real question
is whether we think it's worth fixing, or a good idea to change the
behavior of StringInfo.
StringInfo uses int's to store length, so it could possibly be changed,
but then you'd just error out due to MaxAllocSize.Now perhaps those could both be relaxed, but certainly not to the extent
that you can shove an entire 1.6TB row into an output buffer.
Another way to look at it would be to work in small chunks. For the first test
case (rows bigger than 1GB), maybe the copy command could be rewritten to work
in chunks, flushing the output more often if needed.
For the conversion related issues, I don't really see any other solution than
extending StrinigInfo to allow for more than 1GB of data. On the other hand,
those one can easily be circumvented by using a COPY ... WITH binary.
The other issue is that there's a LOT of places in code that blindly
copy detoasted data around, so while we technically support 1GB toasted
values you're probably going to be quite unhappy with performance. I'm
actually surprised you haven't already seen this with 500MB objects.So long story short, I'm not sure how worthwhile it would be to try and
fix this. We probably should improve the docs though.
I think that having data that can't be output by pg_dump is quite surprising,
and if this is not fixable, I agree that it should clearly be documented.
Have you looked at using large objects for what you're doing? (Note that
those have their own set of challenges and limitations.)
Yes I do. This particular customer of ours did not mind the performance
penalty of using bytea objects as long as it was convenient to use.
We also hit a second issue, this time related to bytea encoding.
There's probably several other places this type of thing could be a
problem. I'm thinking of conversions in particular.
Yes, thats what the two other test cases I mentioned are about: any conversion
leadng to a size greater than 1GB results in an error, even implicit
conversions like doubling antislashes in the output.
--
Ronan Dunklau
http://dalibo.com - http://dalibo.org
On 3/31/15 3:46 AM, Ronan Dunklau wrote:
StringInfo uses int's to store length, so it could possibly be changed,
but then you'd just error out due to MaxAllocSize.Now perhaps those could both be relaxed, but certainly not to the extent
that you can shove an entire 1.6TB row into an output buffer.Another way to look at it would be to work in small chunks. For the first test
case (rows bigger than 1GB), maybe the copy command could be rewritten to work
in chunks, flushing the output more often if needed.
Possibly; I'm not sure how well the FE/BE protocol or code would
actually support that.
The other issue is that there's a LOT of places in code that blindly
copy detoasted data around, so while we technically support 1GB toasted
values you're probably going to be quite unhappy with performance. I'm
actually surprised you haven't already seen this with 500MB objects.So long story short, I'm not sure how worthwhile it would be to try and
fix this. We probably should improve the docs though.I think that having data that can't be output by pg_dump is quite surprising,
and if this is not fixable, I agree that it should clearly be documented.Have you looked at using large objects for what you're doing? (Note that
those have their own set of challenges and limitations.)Yes I do. This particular customer of ours did not mind the performance
penalty of using bytea objects as long as it was convenient to use.
What do they do when they hit 1GB? Presumably if they're this close to
the limit they're already hitting 1GB, no? Or is this mostly hypothetical?
We also hit a second issue, this time related to bytea encoding.
There's probably several other places this type of thing could be a
problem. I'm thinking of conversions in particular.Yes, thats what the two other test cases I mentioned are about: any conversion
leadng to a size greater than 1GB results in an error, even implicit
conversions like doubling antislashes in the output.
I think the big issue with encoding is going to be the risk of changing
encoding and ending up with something too large to fit back into
storage. They might need to consider using something like bytea(990MB).
In any case, I don't think it would be terribly difficult to allow a bit
more than 1GB in a StringInfo. Might need to tweak palloc too; ISTR
there's some 1GB limits there too.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Apr 6, 2015 at 1:51 PM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:
In any case, I don't think it would be terribly difficult to allow a bit
more than 1GB in a StringInfo. Might need to tweak palloc too; ISTR there's
some 1GB limits there too.
The point is, those limits are there on purpose. Changing things
arbitrarily wouldn't be hard, but doing it in a principled way is
likely to require some thought. For example, in the COPY OUT case,
presumably what's happening is that we palloc a chunk for each
individual datum, and then palloc a buffer for the whole row. Now, we
could let the whole-row buffer be bigger, but maybe it would be better
not to copy all of the (possibly very large) values for the individual
columns over into a row buffer before sending it. Some refactoring
that avoids the need for a potentially massive (1.6TB?) whole-row
buffer would be better than just deciding to allow it.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Apr 8, 2015 at 11:53 AM, Robert Haas <robertmhaas@gmail.com> wrote:
On Mon, Apr 6, 2015 at 1:51 PM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:
In any case, I don't think it would be terribly difficult to allow a bit
more than 1GB in a StringInfo. Might need to tweak palloc too; ISTR there's
some 1GB limits there too.The point is, those limits are there on purpose. Changing things
arbitrarily wouldn't be hard, but doing it in a principled way is
likely to require some thought. For example, in the COPY OUT case,
presumably what's happening is that we palloc a chunk for each
individual datum, and then palloc a buffer for the whole row. Now, we
could let the whole-row buffer be bigger, but maybe it would be better
not to copy all of the (possibly very large) values for the individual
columns over into a row buffer before sending it. Some refactoring
that avoids the need for a potentially massive (1.6TB?) whole-row
buffer would be better than just deciding to allow it.
I think that something to be aware of is that this is as well going to
require some rethinking of the existing libpq functions that are here
to fetch a row during COPY with PQgetCopyData, to make them able to
fetch chunks of data from one row.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 4/7/15 10:29 PM, Michael Paquier wrote:
On Wed, Apr 8, 2015 at 11:53 AM, Robert Haas <robertmhaas@gmail.com> wrote:
On Mon, Apr 6, 2015 at 1:51 PM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:
In any case, I don't think it would be terribly difficult to allow a bit
more than 1GB in a StringInfo. Might need to tweak palloc too; ISTR there's
some 1GB limits there too.The point is, those limits are there on purpose. Changing things
arbitrarily wouldn't be hard, but doing it in a principled way is
likely to require some thought. For example, in the COPY OUT case,
presumably what's happening is that we palloc a chunk for each
individual datum, and then palloc a buffer for the whole row. Now, we
could let the whole-row buffer be bigger, but maybe it would be better
not to copy all of the (possibly very large) values for the individual
columns over into a row buffer before sending it. Some refactoring
that avoids the need for a potentially massive (1.6TB?) whole-row
buffer would be better than just deciding to allow it.I think that something to be aware of is that this is as well going to
require some rethinking of the existing libpq functions that are here
to fetch a row during COPY with PQgetCopyData, to make them able to
fetch chunks of data from one row.
The discussion about upping the StringInfo limit was for cases where a
change in encoding blows up because it's now larger. My impression was
that these cases don't expand by a lot, so we wouldn't be significantly
expanding StringInfo.
I agree that buffering 1.6TB of data would be patently absurd. Handling
the case of COPYing a row that's >1GB clearly needs work than just
bumping up some size limits. That's why I was wondering whether this was
a real scenario or just hypothetical... I'd be surprised if someone
would be happy with the performance of 1GB tuples, let alone even larger
than that.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
A customer of ours was hit by this recently and I'd like to get it fixed
for good.
Robert Haas wrote:
On Mon, Apr 6, 2015 at 1:51 PM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:
In any case, I don't think it would be terribly difficult to allow a bit
more than 1GB in a StringInfo. Might need to tweak palloc too; ISTR there's
some 1GB limits there too.The point is, those limits are there on purpose. Changing things
arbitrarily wouldn't be hard, but doing it in a principled way is
likely to require some thought. For example, in the COPY OUT case,
presumably what's happening is that we palloc a chunk for each
individual datum, and then palloc a buffer for the whole row.
Right. There's a serious problem here already, and users are being
bitten by it in existing releases. I think we should provide a
non-invasive fix for back-branches which we could apply as a bug fix.
Here's a proposed patch for the localized fix; it just adds a flag to
StringInfo allowing the string to grow beyond the 1GB limit, but only
for strings which are specifically marked. That way we avoid having to
audit all the StringInfo-using code. In this patch, only the COPY path
is allowed to grow beyond 1GB, which is enough to close the current
problem -- or at least my test case for it.
If others can try this patch to ensure it enables pg_dump to work on
their databases, it would be great.
(In this patch there's a known buglet which is that the UINT64_FORMAT
patched in the error message doesn't allow for translatability.)
Like Robert, I don't like this approach for the long term, however. I
think it would be saner to have CopySendData not keep a single gigantic
string in memory; it would be better to get the bytes out to the client
sooner than end-of-row. To avoid causing a performance hit, we would
only flush when the size of the output buffer is about to reach the
allocation limit (MaxAllocSize); so for regular-sized tuples, we would
only do it at end-of-row, keeping the current behavior. I don't have a
patch for this yet; I'm going to try that next.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
huge-stringinfo.patchtext/x-diff; charset=us-asciiDownload+48-12
Alvaro Herrera wrote:
If others can try this patch to ensure it enables pg_dump to work on
their databases, it would be great.
It doesn't seem to help if one field exceeds 1Gb, for instance when
inflated by a bin->hex translation.
postgres=# create table big as
select pg_read_binary_file('data') as binarycol;
postgres=# select octet_length(binarycol) from big;
octet_length
--------------
1073700000
postgres=# copy big to '/var/tmp/big.copy';
ERROR: XX000: invalid memory alloc request size 2147400003
LOCATION: palloc, mcxt.c:903
Same problem with pg_dump.
OTOH, it improves the case where the cumulative size of field contents
for a row exceeds 1 Gb, but not any single field exceeds that size.
If splitting the table into 3 fields, each smaller than 512MB:
postgres=# create table big2 as select
substring(binarycol from 1 for 300*1024*1024) as b1,
substring(binarycol from 1+300*1024*1024 for 300*1024*1024) as b2 ,
substring(binarycol from 1+600*1024*1024 for 400*1024*1024) as b3
from big;
postgres=# copy big2 to '/var/tmp/big.copy';
COPY 1
then that works, producing a single line of 2097152012 chars
in the output file.
By contrast, it fails with an unpatched 9.5:
postgres=# copy big2 to '/var/tmp/big.copy';
ERROR: 54000: out of memory
DETAIL: Cannot enlarge string buffer containing 629145605 bytes by 629145602
more bytes.
LOCATION: enlargeStringInfo, stringinfo.c:260
If setting bytea_output to 'escape', it also fails with the patch applied,
as it tries to allocate 4x the binary field size, and it exceeds 1GB again.
postgres=# set bytea_output =escape;
SET
postgres=# copy big2 to '/var/tmp/big.copy';
ERROR: invalid memory alloc request size 1258291201
LOCATION: palloc, mcxt.c:821
1258291201 = 300*1024*1024*4+1
Also, the COPY of both tables work fine if using (FORMAT BINARY),
on both the patched and unpatched server.
Best regards,
--
Daniel Vérité
PostgreSQL-powered mailer: http://www.manitou-mail.org
Twitter: @DanielVerite
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
"Daniel Verite" <daniel@manitou-mail.org> writes:
Alvaro Herrera wrote:
If others can try this patch to ensure it enables pg_dump to work on
their databases, it would be great.
It doesn't seem to help if one field exceeds 1Gb, for instance when
inflated by a bin->hex translation.
It's not going to be possible to fix that without enormously invasive
changes (affecting individual datatype I/O functions, for example).
And in general, fields approaching that size are going to give you
problems in all kinds of ways, not only COPY.
I think we should be satisfied if we can make COPY deal with the sum
of a line's fields exceeding 1GB.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I wrote:
If splitting the table into 3 fields, each smaller than 512MB:
postgres=# create table big2 as select
substring(binarycol from 1 for 300*1024*1024) as b1,
substring(binarycol from 1+300*1024*1024 for 300*1024*1024) as b2 ,
substring(binarycol from 1+600*1024*1024 for 400*1024*1024) as b3
from big;
I've tried adding another large field to see what happens if the whole row
exceeds 2GB, and data goes to the client rather than to a file.
postgres=# alter table big2 add b4 bytea;
postgres=# update big2 set b4=b1;
So big2 has 4 bytea columns with 300+300+400+300 MB on a single row.
Then I'm trying to \copy this from a 9.5.1 backend with patch applied,
configured with --enable-debug, on Debian8 x86-64 with 64GB of RAM
and all defaults in postgresql.conf
My idea was to check if the client side was OK with that much data on
a single COPY row, but it turns out that the server is not OK anyway.
postgres=# \copy big2 to /dev/null
lost synchronization with server: got message type "d", length -1568669676
The backend aborts with the following backtrace:
Program terminated with signal 6, Aborted.
#0 0x00007f82ee68e165 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007f82ee6913e0 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00007f82ee6c839b in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#3 0x00007f82ee6d1be6 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#4 0x00007f82ee6d698c in free () from /lib/x86_64-linux-gnu/libc.so.6
#5 0x00000000007b5a89 in AllocSetDelete (context=0xffffffffffffffff)
at aset.c:643
#6 0x00000000007b63e3 in MemoryContextDelete (context=0x1fa58c8) at
mcxt.c:229
#7 0x000000000055fa25 in CopyTo (cstate=0x1fb1050) at copy.c:1967
#8 DoCopyTo (cstate=cstate@entry=0x1fb1050) at copy.c:1778
#9 0x0000000000562ea9 in DoCopy (stmt=stmt@entry=0x1f796d0,
queryString=queryString@entry=0x1f78c60 "COPY big2 TO STDOUT ",
processed=processed@entry=0x7fff22103338) at copy.c:961
#10 0x00000000006b4440 in standard_ProcessUtility (parsetree=0x1f796d0,
queryString=0x1f78c60 "COPY big2 TO STDOUT ", context=<optimized out>,
params=0x0, dest=0x1f79a30, completionTag=0x7fff22103680 "")
at utility.c:541
#11 0x00000000006b1937 in PortalRunUtility (portal=0x1f26680,
utilityStmt=0x1f796d0, isTopLevel=1 '\001', dest=0x1f79a30,
completionTag=0x7fff22103680 "") at pquery.c:1183
#12 0x00000000006b2535 in PortalRunMulti (portal=portal@entry=0x1f26680,
isTopLevel=isTopLevel@entry=1 '\001', dest=dest@entry=0x1f79a30,
altdest=altdest@entry=0x1f79a30,
completionTag=completionTag@entry=0x7fff22103680 "") at pquery.c:1314
#13 0x00000000006b3022 in PortalRun (portal=portal@entry=0x1f26680,
count=count@entry=9223372036854775807,
isTopLevel=isTopLevel@entry=1 '\001', dest=dest@entry=0x1f79a30,
altdest=altdest@entry=0x1f79a30,
completionTag=completionTag@entry=0x7fff22103680 "") at pquery.c:812
#14 0x00000000006b0393 in exec_simple_query (
query_string=0x1f78c60 "COPY big2 TO STDOUT ") at postgres.c:1104
#15 PostgresMain (argc=<optimized out>, argv=argv@entry=0x1f0d240,
dbname=0x1f0d0f0 "postgres", username=<optimized out>) at postgres.c:4030
#16 0x000000000047072b in BackendRun (port=0x1f2d230) at postmaster.c:4239
#17 BackendStartup (port=0x1f2d230) at postmaster.c:3913
#18 ServerLoop () at postmaster.c:1684
#19 0x000000000065b8cd in PostmasterMain (argc=argc@entry=3,
argv=argv@entry=0x1f0c330) at postmaster.c:1292
#20 0x0000000000471161 in main (argc=3, argv=0x1f0c330) at main.c:223
The server log has this:
STATEMENT: COPY big2 TO STDOUT
*** glibc detected *** postgres: postgres postgres [local] COPY: double free
or corruption (out): 0x00007f8234929010 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x75be6)[0x7f82ee6d1be6]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x6c)[0x7f82ee6d698c]
postgres: postgres postgres [local] COPY[0x7b5a89]
postgres: postgres postgres [local] COPY(MemoryContextDelete+0x43)[0x7b63e3]
postgres: postgres postgres [local] COPY[0x55fa25]
postgres: postgres postgres [local] COPY(DoCopy+0x479)[0x562ea9]
postgres: postgres postgres [local]
COPY(standard_ProcessUtility+0x590)[0x6b4440]
postgres: postgres postgres [local] COPY[0x6b1937]
postgres: postgres postgres [local] COPY[0x6b2535]
postgres: postgres postgres [local] COPY(PortalRun+0x202)[0x6b3022]
postgres: postgres postgres [local] COPY(PostgresMain+0x1493)[0x6b0393]
postgres: postgres postgres [local] COPY[0x47072b]
postgres: postgres postgres [local] COPY(PostmasterMain+0xe7d)[0x65b8cd]
postgres: postgres postgres [local] COPY(main+0x3e1)[0x471161]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xfd)[0x7f82ee67aead]
postgres: postgres postgres [local] COPY[0x4711c9]
I will try other tests without bytea exported in text format but with
several huge text columns.
Best regards,
--
Daniel Vérité
PostgreSQL-powered mailer: http://www.manitou-mail.org
Twitter: @DanielVerite
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
"Daniel Verite" <daniel@manitou-mail.org> writes:
I've tried adding another large field to see what happens if the whole row
exceeds 2GB, and data goes to the client rather than to a file.
My idea was to check if the client side was OK with that much data on
a single COPY row, but it turns out that the server is not OK anyway.
BTW, is anyone checking the other side of this, ie "COPY IN" with equally
wide rows? There doesn't seem to be a lot of value in supporting dump
if you can't reload ...
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I wrote:
postgres=# \copy big2 to /dev/null
lost synchronization with server: got message type "d", length -1568669676The backend aborts with the following backtrace:
Program terminated with signal 6, Aborted.
#0 0x00007f82ee68e165 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007f82ee6913e0 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00007f82ee6c839b in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#3 0x00007f82ee6d1be6 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#4 0x00007f82ee6d698c in free () from /lib/x86_64-linux-gnu/libc.so.6
#5 0x00000000007b5a89 in AllocSetDelete (context=0xffffffffffffffff)
at aset.c:643
#6 0x00000000007b63e3 in MemoryContextDelete (context=0x1fa58c8) at
mcxt.c:229
#7 0x000000000055fa25 in CopyTo (cstate=0x1fb1050) at copy.c:1967
The cause of the crash turns out to be, in enlargeStringInfo():
needed += str->len + 1; /* total space required now */
needed is an int and str->len is an int64, so it overflows when the
size has to grow beyond 2^31 bytes, fails to enlarge the buffer and
overwrites memory after it.
When fixing it with a local int64 copy of the variable, the backend
no longer crashes and COPY big2 TO 'file' appears to work.
However, getting it to the client with \copy big2 to 'file'
still produces the error in psql:
lost synchronization with server: got message type "d"
and leaves an empty file, so there are more problems to solve to
go beyond 2GB text per row.
Or maybe another approach would be to advertise that this is the maximum
for a row in text mode, and limit the backend's string buffer to this size
for the time being? Notwithstanding that there's still the other direction
client->server to test.
Best regards,
--
Daniel Vérité
PostgreSQL-powered mailer: http://www.manitou-mail.org
Twitter: @DanielVerite
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 03/02/2016 03:18 PM, Daniel Verite wrote:
I wrote:
postgres=# \copy big2 to /dev/null
lost synchronization with server: got message type "d", length -1568669676The backend aborts with the following backtrace:
Program terminated with signal 6, Aborted.
#0 0x00007f82ee68e165 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007f82ee6913e0 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00007f82ee6c839b in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#3 0x00007f82ee6d1be6 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#4 0x00007f82ee6d698c in free () from /lib/x86_64-linux-gnu/libc.so.6
#5 0x00000000007b5a89 in AllocSetDelete (context=0xffffffffffffffff)
at aset.c:643
#6 0x00000000007b63e3 in MemoryContextDelete (context=0x1fa58c8) at
mcxt.c:229
#7 0x000000000055fa25 in CopyTo (cstate=0x1fb1050) at copy.c:1967The cause of the crash turns out to be, in enlargeStringInfo():
needed += str->len + 1; /* total space required now */
needed is an int and str->len is an int64, so it overflows when the
size has to grow beyond 2^31 bytes, fails to enlarge the buffer and
overwrites memory after it.When fixing it with a local int64 copy of the variable, the backend
no longer crashes and COPY big2 TO 'file' appears to work.However, getting it to the client with \copy big2 to 'file'
still produces the error in psql:
lost synchronization with server: got message type "d"
and leaves an empty file, so there are more problems to solve to
go beyond 2GB text per row.
My guess is this is a problem at the protocol level - the 'd' message is
CopyData, and all the messages use int32 to define length. So if there's
a 2GB row, it's likely to overflow.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:
On 03/02/2016 03:18 PM, Daniel Verite wrote:
However, getting it to the client with \copy big2 to 'file'
still produces the error in psql:
lost synchronization with server: got message type "d"
and leaves an empty file, so there are more problems to solve to
go beyond 2GB text per row.
My guess is this is a problem at the protocol level - the 'd' message is
CopyData, and all the messages use int32 to define length. So if there's
a 2GB row, it's likely to overflow.
I'm too lazy to check the exact wording, but I don't think there's a hard
and fast promise in the protocol doc that one CopyData message == one row.
So we could probably subdivide a very wide line into multiple messages.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 03/02/2016 04:23 PM, Tom Lane wrote:
Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:
On 03/02/2016 03:18 PM, Daniel Verite wrote:
However, getting it to the client with \copy big2 to 'file'
still produces the error in psql:
lost synchronization with server: got message type "d"
and leaves an empty file, so there are more problems to solve to
go beyond 2GB text per row.My guess is this is a problem at the protocol level - the 'd' message is
CopyData, and all the messages use int32 to define length. So if there's
a 2GB row, it's likely to overflow.I'm too lazy to check the exact wording, but I don't think there's a hard
and fast promise in the protocol doc that one CopyData message == one row.
So we could probably subdivide a very wide line into multiple messages.
Well, actually we claim this [1]http://www.postgresql.org/docs/9.5/static/protocol-message-formats.html:
Data that forms part of a COPY data stream. Messages sent from the
backend will always correspond to single data rows, but messages
sent by frontends might divide the data stream arbitrarily.
So that suggests 1:1 messages to rows, although I'm not sure how
difficult would it be to relax this (or how much the clients might rely
on this).
[1]: http://www.postgresql.org/docs/9.5/static/protocol-message-formats.html
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Daniel Verite wrote:
The cause of the crash turns out to be, in enlargeStringInfo():
needed += str->len + 1; /* total space required now */
needed is an int and str->len is an int64, so it overflows when the
size has to grow beyond 2^31 bytes, fails to enlarge the buffer and
overwrites memory after it.When fixing it with a local int64 copy of the variable, the backend
no longer crashes and COPY big2 TO 'file' appears to work.
Great, thanks for debugging.
However, getting it to the client with \copy big2 to 'file'
still produces the error in psql:
lost synchronization with server: got message type "d"
and leaves an empty file, so there are more problems to solve to
go beyond 2GB text per row.
Well, the CopyData message has an Int32 field for the message length.
I don't know the FE/BE protocol very well but I suppose each row
corresponds to one CopyData message, or perhaps each column corresponds
to one CopyData message. In either case, it's not possible to go beyond
2GB without changing the protocol ...
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Tomas Vondra wrote:
My guess is this is a problem at the protocol level - the 'd' message is
CopyData, and all the messages use int32 to define length. So if there's
a 2GB row, it's likely to overflow.
Yes. Besides, the full message includes a negative length:
postgres=# \copy big2 to /dev/null
lost synchronization with server: got message type "d", length -1568669676
which happens to be the correct size if interpreted as an unsigned int32
-1568669676 = (int) (1300UL*1024*1024*2 + 3 + 3*4 + 1 + 4)
One interpretation would be that putting an unsigned length in
CopyData message is a protocol violation.
However it's not clear to me that Int32 in the doc necessarily designates
a signed integer.
Int32 is defined as:
Intn(i)
An n-bit integer in network byte order (most significant byte
first). If i is specified it is the exact value that will appear,
otherwise the value is variable. Eg. Int16, Int32(42).
There's a least one example when we use Int16 as unsigned:
the number of parameters in Bind (F) can be up to 65535.
This maximum is tested explicitly and refered to at several
places in fe-exec.
In some instances, Int32 is clearly signed, because -1 is accepted
to indicate NULLness, such as again in Bind (F) for the length of
the parameter value.
From this it seems to me that Intn is to be interpreted as
signed or unsigned on a case by case basis.
Back to CopyData (F & B), it's documented as:
Byte1('d')
Identifies the message as COPY data.
Int32
Length of message contents in bytes, including self.
Byten
Data that forms part of a COPY data stream. Messages sent from the
backend will always correspond to single data rows, but messages sent
by frontends might divide the data stream arbitrarily.
I don't see any hint that this length is signed, nor any reason of having
it signed.
I guess before the patch it didn't matter, for the B case at least,
because the backend never sent more than 1GB.
Best regards,
--
Daniel Vérité
PostgreSQL-powered mailer: http://www.manitou-mail.org
Twitter: @DanielVerite
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Mar 3, 2016 at 12:47 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:
Well, the CopyData message has an Int32 field for the message length.
I don't know the FE/BE protocol very well but I suppose each row
corresponds to one CopyData message, or perhaps each column corresponds
to one CopyData message. In either case, it's not possible to go beyond
2GB without changing the protocol ...
Based on what I know from this stuff (OOM libpq and other stuff
remnants), one 'd' message means one row. fe-protocol3.c and
CopySendEndOfRow in backend's copy.c are confirming that as well. I am
indeed afraid that having extra logic to get chunks of data will
require extending the protocol with a new message type for this
purpose.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Tom Lane wrote:
BTW, is anyone checking the other side of this, ie "COPY IN" with equally
wide rows? There doesn't seem to be a lot of value in supporting dump
if you can't reload ...
Indeed, the lines bigger than 1GB can't be reloaded :(
My test: with a stock 9.5.1 plus Alvaro's patch with my additional
int64 fix mentioned upthread, creating this table and filling
all columns with 200MB of text each:
create table bigtext(t1 text ,t2 text, t3 text, t4 text,
t5 text, t6 text, t7 text);
# \copy bigtext to '/var/tmp/bigtext.sql'
That part does work as expected, producing this huge single line:
$ wc /var/tmp/bigtext.sql
1 7 1468006407 /var/tmp/bigtext.sql
But reloading it fails:
# create table bigtext2 (like bigtext);
CREATE TABLE
# copy bigtext2 from '/var/tmp/bigtext.sql';
ERROR: 54000: out of memory
DETAIL: Cannot enlarge string buffer containing 1073676288 bytes by 65536
more bytes.
CONTEXT: COPY bigtext2, line 1
LOCATION: enlargeStringInfo, stringinfo.c:278
# \copy bigtext2 from '/var/tmp/bigtext.sql'
ERROR: 54000: out of memory
DETAIL: Cannot enlarge string buffer containing 1073741808 bytes by 8191
more bytes.
CONTEXT: COPY bigtext2, line 1
LOCATION: enlargeStringInfo, stringinfo.c:278
Best regards,
--
Daniel Vérité
PostgreSQL-powered mailer: http://www.manitou-mail.org
Twitter: @DanielVerite
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers