File Coverage

blib/lib/Class/DBI/MSSQL.pm
Criterion Covered Total %
statement 9 60 15.0
branch 0 20 0.0
condition 0 9 0.0
subroutine 3 9 33.3
pod 3 3 100.0
total 15 101 14.8


line stmt bran cond sub pod time code
1 1     1   80298 use strict;
  1         3  
  1         43  
2 1     1   5 use warnings;
  1         2  
  1         46  
3              
4             package Class::DBI::MSSQL;
5 1     1   5 use base qw(Class::DBI);
  1         5  
  1         1464  
6              
7             our $VERSION = '0.122';
8              
9             =head1 NAME
10              
11             Class::DBI::MSSQL - Class::DBI for MSSQL
12              
13             =head1 VERSION
14              
15             version 0.122
16              
17             $Id: /my/cs/projects/cdbi-mssql/trunk/lib/Class/DBI/MSSQL.pm 27829 2006-11-11T04:02:42.956483Z rjbs $
18              
19             =head1 SYNOPSIS
20              
21             use base qw(Class::DBI::MSSQL);
22              
23             # lots of normal-looking CDBI code
24              
25             =head1 DESCRIPTION
26              
27             This is just a simple subclass of Class::DBI; it makes Class::DBI play nicely
28             with MSSQL, at least if DBD::ODBC is providing the connection.
29              
30             Here are the things it changes:
31              
32             =over 4
33              
34             =item * use C
35              
36             =item * use C for C
37              
38             =back
39              
40             It also implements some metadata methods, described below.
41              
42             =cut
43              
44             sub _auto_increment_value {
45 0     0     my $self = shift;
46 0           my $dbh = $self->db_Main;
47              
48 0           my ($id) = $dbh->selectrow_array('SELECT @@IDENTITY');
49 0 0         $self->_croak("Can't get last insert id") unless defined $id;
50 0           return $id;
51             }
52              
53             sub _insert_row {
54 0     0     my $self = shift;
55 0           my $data = shift;
56 0 0         if (keys %$data) {
57 0           return $self->SUPER::_insert_row($data);
58             } else {
59 0           eval {
60 0           my $sth = $self->sql_MakeNewEmptyObj();
61 0           $sth->execute;
62 0           my @primary_columns = $self->primary_columns;
63 0 0 0       $data->{ $primary_columns[0] } = $self->_auto_increment_value
64             if @primary_columns == 1
65             && !defined $data->{ $primary_columns[0] };
66             };
67 0 0         if ($@) {
68 0           my $class = ref $self;
69 0           return $self->_croak(
70             "Can't insert new $class: $@",
71             err => $@,
72             method => 'create'
73             );
74             }
75 0           return 1;
76             }
77             }
78              
79             __PACKAGE__->set_sql(MakeNewEmptyObj => 'INSERT INTO __TABLE__ DEFAULT VALUES');
80              
81             =head1 METHODS
82              
83             =head2 C<< set_up_table($table_name) >>
84              
85             This method sets up the columns from the named table by querying MSSQL's
86             C metadata tables. It will set up the key(s) as Primary
87             and all other columns as Essential.
88              
89             =cut
90              
91             __PACKAGE__->set_sql(desc_table => <<'SQL');
92             SELECT col.table_name, col.column_name, col.data_type, ccu.constraint_name
93             FROM information_schema.columns col
94             LEFT JOIN information_schema.constraint_column_usage ccu
95             ON col.table_catalog = ccu.table_catalog
96             AND col.table_schema = ccu.table_schema
97             AND col.table_name = ccu.table_name
98             AND col.column_name = ccu.column_name
99             WHERE (col.table_name = '__TABLE__')
100             SQL
101              
102             sub set_up_table {
103 0     0 1   my $class = shift;
104 0   0       $class->table(shift || $class->table);
105 0           (my $sth = $class->sql_desc_table)->execute;
106 0           my (@cols, @pri);
107 0           while (my $hash = $sth->fetch_hash) {
108 0           my ($col) = $hash->{column_name} =~ /(\w+)/;
109 0 0         if($hash->{constraint_name} =~ /^PK_/) {
110 0           push @pri, $col;
111             } else {
112 0           push @cols, $col;
113             }
114             }
115 0 0         $class->_croak($class->table, " has no primary key") unless @pri;
116 0           $class->columns(Primary => @pri);
117 0           $class->columns(Essential => @cols);
118             }
119              
120             =head2 C<< column_type($column_name) >>
121              
122             This returns the named column's datatype.
123              
124             =cut
125              
126             sub _column_info {
127 0     0     my $self = shift;
128 0           my $dbh = $self->db_Main;
129            
130 0           (my $sth = $self->sql_desc_table)->execute;
131 0           return { map { $_->{column_name} => $_ } $sth->fetchall_hash };
  0            
132             }
133              
134             sub column_type {
135 0     0 1   my $class = shift;
136 0 0         my $col = shift or Carp::croak "Need a column for column_type";
137 0           return $class->_column_info->{$col}->{data_type};
138             }
139              
140             =head2 C<< autoinflate($type => $class) >>
141              
142             This will automatically set up has_a() relationships for all columns of
143             the specified type to the given class. If the type is "dates" it will apply to
144             both datetime and smalldatetime columns. If the class is Time::Piece,
145             Time::Piece::MSSQL will be required.
146              
147             We currently assume that all classess passed will be able to inflate
148             and deflate without needing extra has_a arguments.
149              
150             =cut
151              
152             sub autoinflate {
153 0     0 1   my ($class, %how) = @_;
154 0   0       $how{$_} ||= $how{dates} for qw/datetime smalldatetime/;
155 0           my $info = $class->_column_info;
156 0           foreach my $col (keys %$info) {
157 0           (my $type = $info->{$col}->{type}) =~ s/\W.*//;
158 0 0         next unless $how{$type};
159 0           my %args;
160 0 0         if ($how{$type} eq "Time::Piece") {
161 0           eval "use Time::Piece::MSSQL";
162 0 0         $class->_croak($@) if $@;
163 0           $args{inflate} = "from_mssql_$type";
164 0           $args{deflate} = "mssql_$type";
165             }
166 0           $class->has_a($col => $how{$type}, %args);
167             }
168             }
169              
170              
171             =head1 WARNINGS
172              
173             For one thing, there are no useful tests in this distribution. I'll take care
174             of that, but right now this is all taken care of in the tests I've written for
175             subclasses of this class, and I don't have a lot of motivation to write new
176             tests just for this package.
177              
178             Class::DBI's C<_init> sub has a line that reads as follows:
179              
180             if (@primary_columns == grep defined, @{$data}{@primary_columns}) {
181              
182             This will cause the primary key columns to autovivify as I, which will
183             make inserts fail under MSSQL. You should change that line to the following,
184             which will fix the behavior.
185              
186             if (@$data{@primary_columns}
187             and @primary_columns == grep defined, @{$data}{@primary_columns}
188             ) {
189              
190             I can't easily subclass that routine, as it relies on lexical variables above
191             its scope. I've sent a patch to Tony, which I expect to be in the next
192             Class::DBI release.
193              
194             =head1 THANKS
195              
196             ...to James O'Sullivan, for graciously sending me his own solution to this
197             problem, which I've happily included.
198              
199             ...to Michael Schwern and Tony Bowden for creating and maintaining,
200             respectively, the excellent Class::DBI system.
201              
202             ...to Casey West, for his crash course on Class::DBI at OSCON '04, which
203             finally convinced me to just use the darn thing.
204              
205             =head1 AUTHOR
206              
207             Ricardo SIGNES, >
208              
209             C and C from James O'Sullivan.
210              
211             =head1 COPYRIGHT
212              
213             (C) 2004-2006, Ricardo SIGNES. Class::DBI::MSSQL is available under the same
214             terms as Perl itself.
215              
216             =cut
217              
218             1;