$self->dbh_do('_dbh_last_insert_id', $seq);
}
+sub _get_pg_search_path {
+ my ($self,$dbh) = @_;
+ # cache the search path as ['schema','schema',...] in the storage
+ # obj
+ $self->{_pg_search_path} ||= do {
+ my @search_path;
+ my ($sp_string) = $dbh->selectrow_array('SHOW search_path');
+ while( $sp_string =~ s/("[^"]+"|[^,]+),?// ) {
+ unless( defined $1 and length $1 ) {
+ $self->throw_exception("search path sanity check failed: '$1'")
+ }
+ push @search_path, $1;
+ }
+ \@search_path
+ };
+}
+
sub _dbh_get_autoinc_seq {
my ($self, $dbh, $schema, $table, @pri) = @_;
- while (my $col = shift @pri) {
- my $info = $dbh->column_info(undef,$schema,$table,$col)->fetchrow_hashref;
- if(defined $info->{COLUMN_DEF} and
- $info->{COLUMN_DEF} =~ /^nextval\(+'([^']+)'::(?:text|regclass)\)/) {
- my $seq = $1;
- # may need to strip quotes -- see if this works
- return $seq =~ /\./ ? $seq : $info->{TABLE_SCHEM} . "." . $seq;
- }
+ # get the list of postgres schemas to search. if we have a schema
+ # specified, use that. otherwise, use the search path
+ my @search_path;
+ if( defined $schema and length $schema ) {
+ @search_path = ( $schema );
+ } else {
+ @search_path = @{ $self->_get_pg_search_path($dbh) };
+ }
+
+ foreach my $search_schema (@search_path) {
+ foreach my $col (@pri) {
+ my $info = $dbh->column_info(undef,$search_schema,$table,$col)->fetchrow_hashref;
+ if($info) {
+ # if we get here, we have definitely found the right
+ # column.
+ if( defined $info->{COLUMN_DEF} and
+ $info->{COLUMN_DEF}
+ =~ /^nextval\(+'([^']+)'::(?:text|regclass)\)/i
+ ) {
+ my $seq = $1;
+ return $seq =~ /\./ ? $seq : $info->{TABLE_SCHEM} . "." . $seq;
+ } else {
+ # we have found the column, but cannot figure out
+ # the nextval seq
+ return;
+ }
+ }
+ }
}
return;
}
This class implements autoincrements for PostgreSQL.
+=head1 POSTGRESQL SCHEMA SUPPORT
+
+This supports multiple PostgreSQL schemas, with one caveat: for
+performance reasons, the schema search path is queried the first time it is
+needed and CACHED for subsequent uses.
+
+For this reason, you should do any necessary manipulation of the
+PostgreSQL search path BEFORE instantiating your schema object, or as
+part of the on_connect_do option to connect(), for example:
+
+ my $schema = My::Schema->connect
+ ( $dsn,$user,$pass,
+ { on_connect_do =>
+ [ 'SET search_path TO myschema, foo, public' ],
+ },
+ );
+
=head1 AUTHORS
-Marcus Ramberg <m.ramberg@cpan.org>
+See L<DBIx::Class/CONTRIBUTORS>
=head1 LICENSE
use strict;
-use warnings;
+use warnings;
use Test::More;
use Test::Exception;
plan skip_all => 'Set $ENV{DBICTEST_PG_DSN}, _USER and _PASS to run this test '.
'(note: This test drops and creates tables called \'artist\', \'casecheck\', \'array_test\' and \'sequence_test\''.
' as well as following sequences: \'pkid1_seq\', \'pkid2_seq\' and \'nonpkid_seq\''.
- ' as well as following schemas: \'testschema\'!)'
+ ' as well as following schemas: \'testschema\',\'anothertestschema\'!)'
unless ($dsn && $user);
-
-plan tests => 39;
-
DBICTest::Schema->load_classes( 'Casecheck', 'ArrayTest' );
-my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
+my $schema = DBICTest::Schema->connect($dsn, $user, $pass,);
# Check that datetime_parser returns correctly before we explicitly connect.
SKIP: {
local $SIG{__WARN__} = sub {};
_cleanup ($dbh);
+ my $artist_table_def = <<EOS;
+(
+ artistid serial PRIMARY KEY
+ , name VARCHAR(100)
+ , rank INTEGER NOT NULL DEFAULT '13'
+ , charfield CHAR(10)
+ , arrayfield INTEGER[]
+)
+EOS
$dbh->do("CREATE SCHEMA testschema;");
- $dbh->do("CREATE TABLE testschema.artist (artistid serial PRIMARY KEY, name VARCHAR(100), rank INTEGER NOT NULL DEFAULT '13', charfield CHAR(10), arrayfield INTEGER[]);");
+ $dbh->do("CREATE TABLE testschema.artist $artist_table_def;");
$dbh->do("CREATE TABLE testschema.sequence_test (pkid1 integer, pkid2 integer, nonpkid integer, name VARCHAR(100), CONSTRAINT pk PRIMARY KEY(pkid1, pkid2));");
$dbh->do("CREATE SEQUENCE pkid1_seq START 1 MAXVALUE 999999 MINVALUE 0");
$dbh->do("CREATE SEQUENCE pkid2_seq START 10 MAXVALUE 999999 MINVALUE 0");
$dbh->do("CREATE SEQUENCE nonpkid_seq START 20 MAXVALUE 999999 MINVALUE 0");
ok ( $dbh->do('CREATE TABLE testschema.casecheck (id serial PRIMARY KEY, "name" VARCHAR(1), "NAME" VARCHAR(2), "UC_NAME" VARCHAR(3), "storecolumn" VARCHAR(10));'), 'Creation of casecheck table');
ok ( $dbh->do('CREATE TABLE testschema.array_test (id serial PRIMARY KEY, arrayfield INTEGER[]);'), 'Creation of array_test table');
+ $dbh->do("CREATE SCHEMA anothertestschema;");
+ $dbh->do("CREATE TABLE anothertestschema.artist $artist_table_def;");
+ $dbh->do("CREATE SCHEMA yetanothertestschema;");
+ $dbh->do("CREATE TABLE yetanothertestschema.artist $artist_table_def;");
+ $dbh->do('set search_path=testschema,public');
}
# store_column is called once for create() for non sequence columns
# This is in Core now, but it's here just to test that it doesn't break
$schema->class('Artist')->load_components('PK::Auto');
+cmp_ok( $schema->resultset('Artist')->count, '==', 0, 'this should start with an empty artist table');
+
+{ # test that auto-pk also works with the defined search path by
+ # un-schema-qualifying the table name
+ my $artist_name_save = $schema->source("Artist")->name;
+ $schema->source("Artist")->name("artist");
+
+ my $unq_new;
+ lives_ok {
+ $unq_new = $schema->resultset('Artist')->create({ name => 'baz' });
+ } 'insert into unqualified, shadowed table succeeds';
+
+ is($unq_new && $unq_new->artistid, 1, "and got correct artistid");
+
+ #test with anothertestschema
+ $schema->source('Artist')->name('anothertestschema.artist');
+ my $another_new = $schema->resultset('Artist')->create({ name => 'ribasushi'});
+ is( $another_new->artistid,1, 'got correct artistid for yetanotherschema');
+
+ #test with yetanothertestschema
+ $schema->source('Artist')->name('yetanothertestschema.artist');
+ my $yetanother_new = $schema->resultset('Artist')->create({ name => 'ribasushi'});
+ is( $yetanother_new->artistid,1, 'got correct artistid for yetanotherschema');
+ is( $yetanother_new->artistid,1, 'got correct artistid for yetanotherschema');
+
+ $schema->source("Artist")->name($artist_name_save);
+}
+
my $new = $schema->resultset('Artist')->create({ name => 'foo' });
-is($new->artistid, 1, "Auto-PK worked");
+is($new->artistid, 2, "Auto-PK worked");
$new = $schema->resultset('Artist')->create({ name => 'bar' });
-is($new->artistid, 2, "Auto-PK worked");
+is($new->artistid, 3, "Auto-PK worked");
+
my $test_type_info = {
'artistid' => {
'DROP SEQUENCE pkid2_seq',
'DROP SEQUENCE nonpkid_seq',
'DROP SCHEMA testschema',
+ 'DROP TABLE anothertestschema.artist',
+ 'DROP SCHEMA anothertestschema',
+ 'DROP TABLE yetanothertestschema.artist',
+ 'DROP SCHEMA yetanothertestschema',
) {
eval { $dbh->do ($stat) };
}
}
+done_testing;
+
END { _cleanup($dbh) }