Handling text fields in Sybase

[prev] [thread] [next] [Date index for 2004/07/23]

From: Merijn Broeren
Subject: Handling text fields in Sybase
Date: 14:48 on 23 Jul 2004
Hello,

We've been using Class::DBI for three days now and are having a few
issues when using some more advanced features of Sybase. We are using
Class::DBI::Sybase, which helped use across the first hurdle of column
names. 

Next we wanted to use identity columns in Sybase, which are generated in
the database. Trouble is that Class::DBI wants to write to primary key
fields, so we need to prohibit that. Easily done:

MSDW::MDP::Db::Release->flag_column(IDENTITY => qw/release_id/);

sub _auto_increment_value {
  my $self = shift;
  my $dbh  = $self->db_Main;
  my @primary_columns = $self->primary_columns;

  #The sybase way
  my $id;
  eval {
    #Can't use placeholders (not conditions)
    my $sth = $dbh->prepare("select MAX( $primary_columns[0] ) from " .
                            $self->table);
    $sth->execute();
    $id = ($sth->fetchrow_array)[0];
  };
  $self->_croak("Can't get last insert id") unless defined $id;
  return $id;
}

#Flag columns as either IDENTITY or TEXT for special treatment
sub flag_column {
  my $proto = shift;
  my $class = ref $proto || $proto;
  my $tag = shift || 'NONE';

  #Make ID column virtual
  if($tag eq 'IDENTITY' && @_) {
    $class->columns(TEMP => @_);
  }

  #Set up text columns
  if($tag eq 'TEXT' && @_) {
    $class->_text_col(\@_);
  }
}

So this now works. Next issue is text fields. They cannot be used in
placeholders ('?') with DBD::Sybase. So we override _insert_row and
update :

#Overwritten from Class::DBI to not use placeholders for text columns
sub _insert_row { 
  my $self = shift;
  my $data = shift;
  eval {
    my @columns = keys %$data;
    my %place_holders;

    #Ok - Don't use placeholders for text fields
    foreach my $col (@columns) {
      if($self->_is_text($col)) {
        $place_holders{$col} = "'" . $data->{$col} . "'";
        delete $data->{$col};
      }
      else {
        $place_holders{$col} = $self->_column_placeholder($col);
      }
    }

    my $sth = $self->sql_MakeNewObj(
                                    join(', ', @columns),
                                    join(', ', map $place_holders{$_},
@columns),
                                   );

    $self->_bind_param($sth, \@columns);
    $sth->execute(values %$data);
    my @primary_columns = $self->primary_columns;
    $data->{ $primary_columns[0] } = $self->_auto_increment_value
      if @primary_columns == 1
        && !defined $data->{ $primary_columns[0] };
  };
  if ($@) {
    my $class = ref $self;
    return $self->_croak(
                         "Can't insert new $class: $@",
                         err    => $@,
                         method => 'create'
                        );
  }
  return 1;
}

Oh we need to change the "'" . $data->{$col} . "'" to use DBI quote
ofcourse. So what do you guys think about this approach?

Also, if I declare something a primary key it seems you cannot update it
to another value, as it gets internally changed before the update
happens, so you end up with a where clause looking for the new value
instead of the old one. Is this by design? We now have to delete the row
first, then stick in a new one, which isn't really updating ofcourse.

Regards,
        -- 
        Merijn Broeren | 'I want to understand everything. I want to know every-
Software Geek  |  thing and put it all together to see what it means.'
               | 'Excellent project, it will look very good on your resume.'

Handling text fields in Sybase
Merijn Broeren 14:48 on 23 Jul 2004

Generated at 11:34 on 01 Dec 2004 by mariachi v0.52