package SPOPS::DBI::Pg;

# $Id: Pg.pm,v 3.2 2003/01/02 06:00:23 lachoy Exp $

use strict;
use SPOPS qw( _w DEBUG );

$SPOPS::DBI::Pg::VERSION  = sprintf("%d.%02d", q$Revision: 3.2 $ =~ /(\d+)\.(\d+)/);

use constant PG_SEQUENCE_NEXT    => "NEXTVAL( '%s' )";
use constant PG_SEQUENCE_CURRENT => "SELECT CURRVAL( '%s' )";

sub sql_quote {
    my ( $class, $value, $type, $db ) = @_;
    $db ||= $class->global_datasource_handle;
    unless ( ref $db ) {
        SPOPS::Exception->throw( "No database handle could be found!" );
    }
    return $db->quote( $value, $type );
}

sub sql_current_date     { return 'CURRENT_TIMESTAMP()' }
sub sql_case_insensitive { return '~*' }

sub pre_fetch_id {
    my ( $item, $p ) = @_;
    my ( $seq_name );
    return undef unless ( $item->CONFIG->{increment_field} );
    return undef unless ( $seq_name = $item->CONFIG->{sequence_name} );
    return sprintf( PG_SEQUENCE_NEXT, $seq_name );
}


sub post_fetch_id {
    my ( $item, $p ) = @_;
    return undef unless ( $item->CONFIG->{increment_field} );
    my $seq_name = $item->CONFIG->{sequence_name}
                   || join( '_', $item->CONFIG->{base_table},
                                 $item->CONFIG->{id_field},
                                 'seq' );
    return undef unless ( $seq_name );
    my $sth = $p->{db}->prepare( sprintf( PG_SEQUENCE_CURRENT, $seq_name ) );
    $sth->execute;
    return ($sth->fetchrow_array)[0];
}

1;

__END__

=head1 NAME

SPOPS::DBI::Pg -- PostgreSQL-specific routines for the SPOPS::DBI

=head1 SYNOPSIS

 # In your configuration:

 'myspops' => {
     'isa' => [ qw/ SPOPS::DBI::Pg SPOPS::DBI / ],

     # If you have a SERIAL field, just set increment_field to a true
     # value

     'increment_field' => 1,

     # If you want to specify the name of your sequence (whether using
     # a SERIAL field or not):

     'sequence_name'   => 'myseq',
     ...
 },

=head1 DESCRIPTION

This just implements some Postgres-specific routines so we can
abstract them out.

One of them optionally returns the sequence value of the just-inserted
id field. Of course, this only works if you have a the field marked as
'SERIAL' or using a sequence value in your table:

 CREATE TABLE my_table (
   id  SERIAL,
   ...
 )

or

 CREATE TABLE my_table (
   id int not null primary key,
   ...
 );

 CREATE SEQUENCE myobject_sequence;


You must to let this module know if you are using this option by
setting in your class configuration the key 'increment_field' to a
true value:

 $spops = {
    myobj => {
       class => 'My::Object',
       isa   => [ qw/ SPOPS::DBI::Pg  SPOPS::DBI / ],
       increment_field => 1,
       ...
    },
 };

If you use the 'SERIAL' datatype then you do not have to specify a
sequence name. Otherwise you need to tell SPOPS what sequence to use
in the class configuration:

 $spops = {
    myobj => {
       class           => 'My::Object',
       isa             => [ qw/ SPOPS::DBI::Pg  SPOPS::DBI / ],
       increment_field => 1,
       sequence_name   => 'myobject_sequence',
    },
 };

B<NOTE>: The name automatically created by PostgreSQL when you use the
'SERIAL' datatype follows a certain convention
($table-$idfield-seq). But if the table or ID field are too long,
PostgreSQL will truncate the name so it will fit in the 32-character
limit for symbols. In this case you will either need to recompile
PostgreSQL (yuck) or list the sequence name in the class
configuration. See a message from the openinteract-help mailing list
at:

  http://www.geocrawler.com/archives/3/8429/2002/1/0/7551783/

for more information on recompiling if you are so inclined.

=head1 METHODS

B<sql_current_date()>

Returns 'CURRENT_TIMESTAMP()', used in PostgreSQL to return the value
for right now.

B<sql_quote( $value, $data_type, [ $db_handle ] )>

L<DBD::Pg|DBD::Pg> depends on the type of a field if you are quoting
values to put into a statement, so we override the default 'sql_quote'
from L<SPOPS::SQLInterface|SPOPS::SQLInterface> to ensure the type of
the field is used in the DBI-E<gt>quote call.

The C<$data_type> should correspond to one of the DBI datatypes (see
the file 'dbi_sql.h' in your Perl library tree for more info). If the
DBI database handle C<$db_handle> is not passed in, we try to find it
with the class method C<global_datasource_handle()>.

B<pre_fetch_id( \%params )>

If 'increment_field' is not set we do not fetch an ID. If
'sequence_name' is not also set we do not fetch an ID, assuming that
you have defined the ID field using the 'SERIAL' datatype.

B<post_fetch_id( \%params )>

Retrieve the value just put into the database for the ID field. To use
this you must in the configuration for your object set
'increment_field' to a true value and either specify a 'sequence_name'
or use the SERIAL-default name of:

  <table_name>_<id_field_name>_seq

This is the sequence created by default when you use the 'SERIAL'
datatype.

=head1 BUGS

None known.

=head1 TO DO

Nothing known.

=head1 SEE ALSO

L<DBD::Pg|DBD::Pg>

L<DBI|DBI>

=head1 COPYRIGHT

Copyright (c) 2001-2002 intes.net, inc.. All rights reserved.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 AUTHORS

Chris Winters  E<lt>chris@cwinters.comE<gt>
