Author: turnstep
Date: Sat Mar 21 21:01:43 2009
New Revision: 12621
Modified:
DBD-Pg/trunk/Changes
DBD-Pg/trunk/Pg.pm
DBD-Pg/trunk/dbdimp.c
DBD-Pg/trunk/t/03dbmethod.t
Log:
Allow lo_import and lo_export to work if AutoCommit is enabled.
Per bug #44461, reported by Kynn Jones on the pgsql-general list.
Throw exceptions if any other large object function is called
when AutoCommit is enabled. Add tests for lo_import and lo_export,
clean up and expand the large object tests in general, and add
specific AutoCommit tests.
Modified: DBD-Pg/trunk/Changes
==============================================================================
--- DBD-Pg/trunk/Changes (original)
+++ DBD-Pg/trunk/Changes Sat Mar 21 21:01:43 2009
@@ -2,7 +2,10 @@
2.11.9
- - Fix a memory leak when parsing returned arrays [reported by Bálint Szilakszi]
+ - Throw an exception for large_object functions called when AutoCommit is on,
+ but allow lo_import and lo_export to work. Reported by Kynn Jones.
+ [GSM] (CPAN bug #44461)
+ - Fix a memory leak when parsing returned arrays. Reported by Bálint Szilakszi.
[GSM] (CPAN bug #44225)
- Do proper dequoting of boolean arrays [Armando Santos, GSM] (CPAN bug #43768)
- Fix minor bugs in POD docs. [Frank Wiegand] (CPAN bug #44242)
Modified: DBD-Pg/trunk/Pg.pm
==============================================================================
--- DBD-Pg/trunk/Pg.pm (original)
+++ DBD-Pg/trunk/Pg.pm Sat Mar 21 21:01:43 2009
@@ -2042,7 +2042,7 @@
since Postgres version 8.1. For backwards compatibility, however, you should
set a valid mode anyway (see L</lo_open> for a list of valid modes).
-Upon failure it returns C<undef>.
+Upon failure it returns C<undef>. This function cannot be used if AutoCommit is enabled.
=item lo_open
@@ -2065,21 +2065,21 @@
which was active when C<lo_write> was called.
Returns C<undef> upon failure. Note that 0 is a perfectly correct (and common)
-object descriptor!
+object descriptor! This function cannot be used if AutoCommit is enabled.
=item lo_write
$nbytes = $dbh->func($lobj_fd, $buffer, $len, 'lo_write');
Writes C<$len> bytes of c<$buffer> into the large object C<$lobj_fd>. Returns the number
-of bytes written and C<undef> upon failure.
+of bytes written and C<undef> upon failure. This function cannot be used if AutoCommit is enabled.
=item lo_read
$nbytes = $dbh->func($lobj_fd, $buffer, $len, 'lo_read');
Reads C<$len> bytes into c<$buffer> from large object C<$lobj_fd>. Returns the number of
-bytes read and C<undef> upon failure.
+bytes read and C<undef> upon failure. This function cannot be used if AutoCommit is enabled.
=item lo_lseek
@@ -2087,25 +2087,28 @@
Changes the current read or write location on the large object
C<$obj_id>. Currently C<$whence> can only be 0 (which is L_SET). Returns the current
-location and C<undef> upon failure.
+location and C<undef> upon failure. This function cannot be used if AutoCommit is enabled.
=item lo_tell
$loc = $dbh->func($lobj_fd, 'lo_tell');
Returns the current read or write location on the large object C<$lobj_fd> and C<undef> upon failure.
+This function cannot be used if AutoCommit is enabled.
=item lo_close
$lobj_fd = $dbh->func($lobj_fd, 'lo_close');
Closes an existing large object. Returns true upon success and false upon failure.
+This function cannot be used if AutoCommit is enabled.
=item lo_unlink
$ret = $dbh->func($lobjId, 'lo_unlink');
Deletes an existing large object. Returns true upon success and false upon failure.
+This function cannot be used if AutoCommit is enabled.
=item lo_import
Modified: DBD-Pg/trunk/dbdimp.c
==============================================================================
--- DBD-Pg/trunk/dbdimp.c (original)
+++ DBD-Pg/trunk/dbdimp.c Sat Mar 21 21:01:43 2009
@@ -4088,7 +4088,7 @@
/* ================================================================== */
-/* Used to ensure we are in a txn, e.g. the lo_ functions below */
+/* For lo_* functions. Used to ensure we are in a transaction */
static int pg_db_start_txn (pTHX_ SV * dbh, imp_dbh_t * imp_dbh)
{
int status = -1;
@@ -4096,7 +4096,7 @@
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_start_txn\n", THEADER);
/* If not autocommit, start a new transaction */
- if (!imp_dbh->done_begin && !DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ if (!imp_dbh->done_begin) {
status = _result(aTHX_ imp_dbh, "begin");
if (PGRES_COMMAND_OK != status) {
TRACE_PQERRORMESSAGE;
@@ -4107,160 +4107,270 @@
imp_dbh->done_begin = DBDPG_TRUE;
}
if (TEND) TRC(DBILOGFP, "%sEnd pg_db_start_txn\n", THEADER);
+
return 1;
+
} /* end of pg_db_start_txn */
+/* ================================================================== */
+/* For lo_import and lo_export functions. Used to commit or rollback a
+ transaction, if AutoCommit is on. */
+static int pg_db_end_txn (pTHX_ SV * dbh, imp_dbh_t * imp_dbh, int commit)
+{
+ int status = -1;
+
+ if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_end_txn with %s\n",
+ THEADER, commit ? "commit" : "rollback");
+
+ /* If not autocommit, start a new transaction */
+ status = _result(aTHX_ imp_dbh, commit ? "commit" : "rollback");
+ if (PGRES_COMMAND_OK != status) {
+ TRACE_PQERRORMESSAGE;
+ pg_error(aTHX_ dbh, status, PQerrorMessage(imp_dbh->conn));
+ if (TEND) TRC(DBILOGFP, "%sEnd pg_db_end_txn (error: status not OK for %s)\n",
+ THEADER, commit ? "commit" : "rollback");
+ return 0;
+ }
+
+ imp_dbh->done_begin = DBDPG_FALSE;
+
+ if (TEND) TRC(DBILOGFP, "%sEnd pg_db_end_txn\n", THEADER);
+ return 1;
+
+} /* end of pg_db_end_txn */
+
/* Large object functions */
/* ================================================================== */
unsigned int pg_db_lo_creat (SV * dbh, int mode)
{
+
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_pg_lo_creat (mode: %d)\n", THEADER, mode);
- if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ croak("Cannot call lo_creat when AutoCommit is on");
+ }
+
+ if (!pg_db_start_txn(aTHX_ dbh,imp_dbh)) {
return 0; /* No other option, because lo_creat returns an Oid */
+ }
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_creat\n", THEADER);
}
+
return lo_creat(imp_dbh->conn, mode); /* 0 on error */
+
}
/* ================================================================== */
int pg_db_lo_open (SV * dbh, unsigned int lobjId, int mode)
{
+
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_pg_lo_open (mode: %d objectid: %d)\n",
THEADER, mode, lobjId);
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ croak("Cannot call lo_open when AutoCommit is on");
+ }
+
if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
return -2;
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_open\n", THEADER);
}
+
return lo_open(imp_dbh->conn, lobjId, mode); /* -1 on error */
+
}
/* ================================================================== */
int pg_db_lo_close (SV * dbh, int fd)
{
+
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_lo_close (fd: %d)\n", THEADER, fd);
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ croak("Cannot call lo_close when AutoCommit is on");
+ }
+
+ if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
+ return -1;
+
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_close\n", THEADER);
}
+
return lo_close(imp_dbh->conn, fd); /* <0 on error, 0 if ok */
+
}
/* ================================================================== */
int pg_db_lo_read (SV * dbh, int fd, char * buf, size_t len)
{
+
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_lo_read (fd: %d length: %d)\n",
THEADER, fd, len);
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ croak("Cannot call lo_read when AutoCommit is on");
+ }
+
+ if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
+ return -1;
+
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_read\n", THEADER);
}
+
return lo_read(imp_dbh->conn, fd, buf, len); /* bytes read, <0 on error */
+
}
/* ================================================================== */
int pg_db_lo_write (SV * dbh, int fd, char * buf, size_t len)
{
+
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_lo_write (fd: %d length: %d)\n",
THEADER, fd, len);
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ croak("Cannot call lo_write when AutoCommit is on");
+ }
+
+ if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
+ return -1;
+
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_write\n", THEADER);
}
+
return lo_write(imp_dbh->conn, fd, buf, len); /* bytes written, <0 on error */
+
}
/* ================================================================== */
int pg_db_lo_lseek (SV * dbh, int fd, int offset, int whence)
{
+
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_lo_lseek (fd: %d offset: %d whence: %d)\n",
THEADER, fd, offset, whence);
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ croak("Cannot call lo_lseek when AutoCommit is on");
+ }
+
+ if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
+ return -1;
+
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_lseek\n", THEADER);
}
+
return lo_lseek(imp_dbh->conn, fd, offset, whence); /* new position, -1 on error */
+
}
/* ================================================================== */
int pg_db_lo_tell (SV * dbh, int fd)
{
+
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_lo_tell (fd: %d)\n", THEADER, fd);
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ croak("Cannot call lo_tell when AutoCommit is on");
+ }
+
+ if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
+ return -1;
+
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_tell\n", THEADER);
}
+
return lo_tell(imp_dbh->conn, fd); /* current position, <0 on error */
+
}
/* ================================================================== */
int pg_db_lo_unlink (SV * dbh, unsigned int lobjId)
{
+
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_lo_unlink (objectid: %d)\n", THEADER, lobjId);
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ croak("Cannot call lo_unlink when AutoCommit is on");
+ }
+
if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
- return -2;
+ return -1;
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_unlink\n", THEADER);
}
+
return lo_unlink(imp_dbh->conn, lobjId); /* 1 on success, -1 on failure */
+
}
/* ================================================================== */
unsigned int pg_db_lo_import (SV * dbh, char * filename)
{
+
+ Oid loid;
dTHX;
D_imp_dbh(dbh);
if (TSTART) TRC(DBILOGFP, "%sBegin pg_db_lo_import (filename: %s)\n", THEADER, filename);
- if (!pg_db_start_txn(aTHX_ dbh,imp_dbh)) {
- if (TRACE7) TRC(DBILOGFP, "%sCannot pg_db_log_import unless in a transaction\n",
- THEADER);
+ if (!pg_db_start_txn(aTHX_ dbh,imp_dbh))
return 0; /* No other option, because lo_import returns an Oid */
- }
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_import\n", THEADER);
}
- return lo_import(imp_dbh->conn, filename); /* 0 on error */
+ loid = lo_import(imp_dbh->conn, filename); /* 0 on error */
+
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ if (!pg_db_end_txn(aTHX_ dbh, imp_dbh, 0==loid ? 0 : 1))
+ return 0;
+ }
+
+ return loid;
+
}
/* ================================================================== */
int pg_db_lo_export (SV * dbh, unsigned int lobjId, char * filename)
{
+
+ Oid loid;
dTHX;
D_imp_dbh(dbh);
@@ -4273,7 +4383,13 @@
if (TLIBPQ) {
TRC(DBILOGFP, "%slo_export\n", THEADER);
}
- return lo_export(imp_dbh->conn, lobjId, filename); /* 1 on success, -1 on failure */
+ loid = lo_export(imp_dbh->conn, lobjId, filename); /* 1 on success, -1 on failure */
+ if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) {
+ if (!pg_db_end_txn(aTHX_ dbh, imp_dbh, -1==loid ? 0 : 1))
+ return -1;
+ }
+
+ return loid;
}
Modified: DBD-Pg/trunk/t/03dbmethod.t
==============================================================================
--- DBD-Pg/trunk/t/03dbmethod.t (original)
+++ DBD-Pg/trunk/t/03dbmethod.t Sat Mar 21 21:01:43 2009
@@ -5,8 +5,6 @@
## "data_sources" (see 04misc.t)
## "disconnect" (see 01connect.t)
## "take_imp_data"
-## "lo_import"
-## "lo_export"
## "pg_savepoint", "pg_release", "pg_rollback_to" (see 20savepoints.t)
## "pg_putline", "pg_getline", "pg_endcopy" (see 07copy.t)
@@ -26,7 +24,7 @@
if (! defined $dbh) {
plan skip_all => 'Connection to database failed, cannot continue testing';
}
-plan tests => 479;
+plan tests => 507;
isnt ($dbh, undef, 'Connect to database for database handle method testing');
@@ -1297,27 +1295,33 @@
$t='DB handle method "lo_creat" returns a valid descriptor for reading';
$dbh->{AutoCommit}=1; $dbh->{AutoCommit}=0; ## Catch error where not in begin
+
my ($R,$W) = ($dbh->{pg_INV_READ}, $dbh->{pg_INV_WRITE});
my $RW = $R|$W;
my $object = $dbh->func($R, 'lo_creat');
like ($object, qr/^\d+$/o, $t);
+isnt ($object, 0, $t);
$t='DB handle method "lo_creat" returns a valid descriptor for writing';
$object = $dbh->func($W, 'lo_creat');
like ($object, qr/^\d+$/o, $t);
+isnt ($object, 0, $t);
$t='DB handle method "lo_open" returns a valid descriptor for writing';
my $handle = $dbh->func($object, $W, 'lo_open');
like ($handle, qr/^\d+$/o, $t);
+isnt ($object, -1, $t);
$t='DB handle method "lo_lseek" works when writing';
$result = $dbh->func($handle, 0, 0, 'lo_lseek');
is ($result, 0, $t);
+isnt ($object, -1, $t);
$t='DB handle method "lo_write" works';
my $buf = 'tangelo mulberry passionfruit raspberry plantain' x 500;
$result = $dbh->func($handle, $buf, length($buf), 'lo_write');
is ($result, length($buf), $t);
+cmp_ok ($object, '>', 0, $t);
$t='DB handle method "lo_close" works after write';
$result = $dbh->func($handle, 'lo_close');
@@ -1327,6 +1331,7 @@
$t='DB handle method "lo_open" returns a valid descriptor for reading';
$handle = $dbh->func($object, $R, 'lo_open');
like ($handle, qr/^\d+$/o, $t);
+cmp_ok ($handle, 'eq', 0, $t);
$t='DB handle method "lo_lseek" works when reading';
$result = $dbh->func($handle, 11, 0, 'lo_lseek');
@@ -1350,7 +1355,140 @@
$t='DB handle method "lo_unlink" works';
$result = $dbh->func($object, 'lo_unlink');
-ok ($result, $t);
+is ($result, 1, $t);
+
+$t='DB handle method "lo_unlink" fails when called second time';
+$result = $dbh->func($object, 'lo_unlink');
+ok (!$result, $t);
+$dbh->rollback();
+
+SKIP: {
+
+ eval {
+ require File::Temp;
+ };
+ $@ and skip ('Must have File::Temp to test lo_import and lo_export', 8);
+
+ $t='DB handle method "lo_import" works';
+ my ($fh,$filename) = File::Temp::tmpnam();
+ print $fh "abc\ndef";
+ close $fh or warn 'Failed to close temporary file';
+ my $handle = $dbh->func($filename, 'lo_import');
+ my $objid = $handle;
+ ok ($handle, $t);
+
+ $t='DB handle method "lo_import" inserts correct data';
+ $SQL = "SELECT data FROM pg_largeobject where loid = $handle";
+ $info = $dbh->selectall_arrayref($SQL)->[0][0];
+ is_deeply($info, "abc\ndef", $t);
+
+ $t='DB handle method "lo_open" works after "lo_insert"';
+ $handle = $dbh->func($handle, $R, 'lo_open');
+ like ($handle, qr/^\d+$/o, $t);
+
+ $t='DB handle method "lo_read" returns correct data after "lo_import"';
+ my $data = '';
+ $result = $dbh->func($handle, $data, 100, 'lo_read');
+ is ($result, 7, $t);
+ is ($data, "abc\ndef", $t);
+
+ $t='DB handle method "lo_export" works';
+ ($fh,$filename) = File::Temp::tmpnam();
+ $result = $dbh->func($objid, $filename, 'lo_export');
+ ok(-e $filename, $t);
+ seek($fh,0,1);
+ seek($fh,0,0);
+ $result = read $fh, $data, 10;
+ is ($result, 7, $t);
+ is ($data, "abc\ndef", $t);
+ close $fh or warn 'Could not close tempfile';
+}
+
+## Same tests, but with AutoCommit on
+$dbh->{AutoCommit}=1;
+
+$t='DB handle method "lo_creat" fails when AutoCommit on';
+eval {
+ $dbh->func($W, 'lo_creat');
+};
+like($@, qr{lo_creat when AutoCommit is on}, $t);
+
+$t='DB handle method "lo_open" fails with AutoCommit on';
+eval {
+ $dbh->func($object, $W, 'lo_open');
+};
+like($@, qr{lo_open when AutoCommit is on}, $t);
+
+$t='DB handle method "lo_read" fails with AutoCommit on';
+eval {
+ $dbh->func($object, $data, 0, 'lo_read');
+};
+like($@, qr{lo_read when AutoCommit is on}, $t);
+
+$t='DB handle method "lo_lseek" fails with AutoCommit on';
+eval {
+ $dbh->func($handle, 0, 0, 'lo_lseek');
+};
+like($@, qr{lo_lseek when AutoCommit is on}, $t);
+
+$t='DB handle method "lo_write" fails with AutoCommit on';
+$buf = 'tangelo mulberry passionfruit raspberry plantain' x 500;
+eval {
+ $dbh->func($handle, $buf, length($buf), 'lo_write');
+};
+like($@, qr{lo_write when AutoCommit is on}, $t);
+
+$t='DB handle method "lo_close" fails with AutoCommit on';
+eval {
+ $dbh->func($handle, 'lo_close');
+};
+like($@, qr{lo_close when AutoCommit is on}, $t);
+
+$t='DB handle method "lo_tell" fails with AutoCommit on';
+eval {
+ $dbh->func($handle, 'lo_tell');
+};
+like($@, qr{lo_tell when AutoCommit is on}, $t);
+
+$t='DB handle method "lo_unlink" fails with AutoCommit on';
+eval {
+ $dbh->func($object, 'lo_unlink');
+};
+like($@, qr{lo_unlink when AutoCommit is on}, $t);
+
+
+SKIP: {
+
+ eval {
+ require File::Temp;
+ };
+ $@ and skip ('Must have File::Temp to test lo_import and lo_export', 2);
+
+ $t='DB handle method "lo_import" works (AutoCommit on)';
+ my ($fh,$filename) = File::Temp::tmpnam();
+ print $fh "abc\ndef";
+ close $fh or warn 'Failed to close temporary file';
+ my $handle = $dbh->func($filename, 'lo_import');
+ my $objid = $handle;
+ ok ($handle, $t);
+
+ $t='DB handle method "lo_import" inserts correct data (AutoCommit on)';
+ $SQL = "SELECT data FROM pg_largeobject where loid = $handle";
+ $info = $dbh->selectall_arrayref($SQL)->[0][0];
+ is_deeply($info, "abc\ndef", $t);
+
+ $t='DB handle method "lo_export" works (AutoCommit on)';
+ ($fh,$filename) = File::Temp::tmpnam();
+ $result = $dbh->func($objid, $filename, 'lo_export');
+ ok(-e $filename, $t);
+ seek($fh,0,1);
+ seek($fh,0,0);
+ $result = read $fh, $data, 10;
+ is ($result, 7, $t);
+ is ($data, "abc\ndef", $t);
+ close $fh or warn 'Could not close tempfile';
+}
+$dbh->{AutoCommit} = 0;
#
# Test of the "pg_notifies" database handle method
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.