in reply to Re^2: Appending text to a column in SQL
in thread Appending text to a column in SQL

Sorry about that - PostgreSQL does let you concatenate ('||') text fields. It's been awhile since I had to deal with SQLServer weirdness.

Well then you're either left with running a transaction around your read/append/write or CAST the text field to varchar then concat (assuming the data can be cast).

I would go for the transaction approach, you'll be sure it'll work with any kind of data.

As for the ugliness it should be easy to make a method to hide the $dbh->{AutoCommit} = 0 (or $dbh->begin_work), read, append, update, $dbh->commit. Don't forget to turn AutoCommit off if it's on. In fact I would just add that in there to be safe.

local $dbh->{AutoCommit} = 0; local $dbh->{RaiseError} = 1; # You can RaiseError and eval the transaction # or you can check $sth->err each time and rollback eval { ##Do your read and write here $dbh->commit; }; if ($@) { warn "append failed $@"; $dbh->rollback; }
UPDATE: something similar to this
sub append_to_text { my $dbh = shift; my $table = shift; my $id = shift; my $col = shift; my $data = shift; local $dbh->{RaiseError} = 1; eval { $dbh->begin_work(); my $read = qq| SELECT $col FROM $table WHERE id = ? |; my $update = qq| UPDATE $table SET $col = ? WHERE id = ? |; my $sth_read = $dbh->prepare($read); $sth_read->execute($id); my $row = $sth_read->fetchrow_hashref(); $sth_read->finish(); my $current = $row->{$col}; $current .= $data; my $rtn = $dbh->do($update,undef,$current,$id); $dbh->commit() if ($rtn); }; if ($@) { warn "Append Failed: $@\nStatement: $DBI::lasth->{Statement} +"; eval { $dbh->rollback }; } }


grep
One dead unjugged rabbit fish later

Replies are listed 'Best First'.
Re^4: Appending text to a column in SQL
by sgifford (Prior) on Oct 18, 2006 at 03:06 UTC
    Thanks again grep,

    Are you sure that's right? I thought transactions ensured that multiple changes to the database were either all executed or all aborted, but didn't provide any guarantees that about data that was read. Some quick tests seem to confirm this. For example, if I run multiple copies of this program at the same time, the last one to finish clobbers the values written by any previous runs:

    #!/usr/bin/perl use warnings; use strict; use constant DSN => 'DBI:mysql:database=test'; use constant DBUSER => 'user'; use constant DBPASS => 'pass'; use DBI; our $dbh = DBI->connect(DSN, DBUSER, DBPASS, { AutoCommit => 0, RaiseError => 1, } ) or die "Couldn't connect to database: ",$DBI::errstr; # Create the message and get the ID my $sth = $dbh->prepare("SELECT a FROM transtest"); $sth->execute(); my $row = $sth->fetchrow_hashref; warn "$$ a=$row->{a}\n"; sleep(5); $dbh->do("UPDATE transtest SET a = ?",undef,$row->{a}+1); warn "$$ done\n";
      It definately should lock records involved in a transaction. It is the most important purpose of transactions. There are only 2 reasons it wouldn't do locking during your transaction.

      1. your Transaction Isolation Level. You may need to modify the Transaction Isolation Level above 'READ COMMITED'. BTW running at lower levels is bad.

      2. This all goes out the window though if your DBD doesn't use SQLServer transactions and impliments it's own. I don't know what DBD you're using, but this should be easy to verify. You can read the DBD or rewrite your sql to include 'BEGIN TRANSACTION;' at the beginning of your first statement and ';COMMIT TRANSACTION;' at the end of your last. This way you know that the SQLServer transactions are called. Then test it out.

      UPDATE: Clarify things a little



      grep
      One dead unjugged rabbit fish later