Another overhaul of transaction/savepoint handling
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / ADO / MS_Jet.pm
1 package DBIx::Class::Storage::DBI::ADO::MS_Jet;
2
3 use strict;
4 use warnings;
5 use base qw/
6   DBIx::Class::Storage::DBI::ADO
7   DBIx::Class::Storage::DBI::ACCESS
8 /;
9 use mro 'c3';
10 use DBIx::Class::Storage::DBI::ADO::MS_Jet::Cursor ();
11
12 __PACKAGE__->cursor_class('DBIx::Class::Storage::DBI::ADO::MS_Jet::Cursor');
13
14 =head1 NAME
15
16 DBIx::Class::Storage::DBI::ADO::MS_Jet - Support for MS Access over ADO
17
18 =head1 DESCRIPTION
19
20 This driver is a subclass of L<DBIx::Class::Storage::DBI::ADO> and
21 L<DBIx::Class::Storage::DBI::ACCESS> for connecting to MS Access via
22 L<DBD::ADO>.
23
24 See the documentation for L<DBIx::Class::Storage::DBI::ACCESS> for
25 information on the MS Access driver for L<DBIx::Class>.
26
27 This driver implements workarounds for C<TEXT/IMAGE/MEMO> columns, sets the
28 L<cursor_class|DBIx::Class::Storage::DBI/cursor_class> to
29 L<DBIx::Class::Storage::DBI::ADO::MS_Jet::Cursor> to normalize returned
30 C<GUID> values and provides L<DBIx::Class::InflateColumn::DateTime> support
31 for C<DATETIME> columns.
32
33 =head1 EXAMPLE DSNs
34
35   # older Access versions:
36   dbi:ADO:Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\rkitover\Documents\access_sample.accdb
37
38   # newer Access versions:
39   dbi:ADO:Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\rkitover\Documents\access_sample.accdb;Persist Security Info=False'
40
41 =head1 TEXT/IMAGE/MEMO COLUMNS
42
43 The ADO driver does not suffer from the
44 L<problems|DBIx::Class::Storage::DBI::ODBC::ACCESS/"TEXT/IMAGE/MEMO COLUMNS">
45 the L<ODBC|DBIx::Class::Storage::DBI::ODBC::ACCESS> driver has with these types
46 of columns. You can use them safely.
47
48 When you execute a C<CREATE TABLE> statement over this driver with a C<TEXT>
49 column, it will be converted to C<MEMO>, while in the
50 L<ODBC|DBIx::Class::Storage::DBI::ODBC::ACCESS> driver it is converted to
51 C<VARCHAR(255)>.
52
53 However, the caveat about L<LongReadLen|DBI/LongReadLen> having to be twice the
54 max size of your largest C<MEMO/TEXT> column C<+1> still applies. L<DBD::ADO>
55 sets L<LongReadLen|DBI/LongReadLen> to a large value by default, so it should be
56 safe to just leave it unset. If you do pass a L<LongReadLen|DBI/LongReadLen> in
57 your L<connect_info|DBIx::Class::Storage::DBI/connect_info>, it will be
58 multiplied by two and C<1> added, just as for the
59 L<ODBC|DBIx::Class::Storage::DBI::ODBC::ACCESS> driver.
60
61 =cut
62
63 # set LongReadLen = LongReadLen * 2 + 1 (see docs on MEMO)
64 sub _run_connection_actions {
65   my $self = shift;
66
67   my $long_read_len = $self->_dbh->{LongReadLen};
68
69 # This is the DBD::ADO default.
70   if ($long_read_len != 2147483647) {
71     $self->_dbh->{LongReadLen} = $long_read_len * 2 + 1;
72   }
73
74   return $self->next::method(@_);
75 }
76
77 # AutoCommit does not get reset properly after transactions for some reason
78 # (probably because of my nested transaction hacks in ACCESS.pm) fix it up
79 # here.
80
81 sub _exec_txn_commit {
82   my $self = shift;
83   $self->next::method(@_);
84   $self->_dbh->{AutoCommit} = $self->_dbh_autocommit
85     if $self->{transaction_depth} == 1;
86 }
87
88 sub _exec_txn_rollback {
89   my $self = shift;
90   $self->next::method(@_);
91   $self->_dbh->{AutoCommit} = $self->_dbh_autocommit
92     if $self->{transaction_depth} == 1;
93 }
94
95 # Fix up GUIDs for ->find, for cursors see the cursor_class above.
96
97 sub select_single {
98   my $self = shift;
99   my ($ident, $select) = @_;
100
101   my @row = $self->next::method(@_);
102
103   return @row unless
104     $self->cursor_class->isa('DBIx::Class::Storage::DBI::ADO::MS_Jet::Cursor');
105
106   my $col_info = $self->_resolve_column_info($ident);
107
108   for my $select_idx (0..$#$select) {
109     my $selected = $select->[$select_idx];
110
111     next if ref $selected;
112
113     my $data_type = $col_info->{$selected}{data_type};
114
115     if ($self->_is_guid_type($data_type)) {
116       my $returned = $row[$select_idx];
117
118       $row[$select_idx] = substr($returned, 1, 36)
119         if substr($returned, 0, 1) eq '{';
120     }
121   }
122
123   return @row;
124 }
125
126 sub datetime_parser_type {
127   'DBIx::Class::Storage::DBI::ADO::MS_Jet::DateTime::Format'
128 }
129
130 package # hide from PAUSE
131   DBIx::Class::Storage::DBI::ADO::MS_Jet::DateTime::Format;
132
133 my $datetime_format = '%m/%d/%Y %I:%M:%S %p';
134 my $datetime_parser;
135
136 sub parse_datetime {
137   shift;
138   require DateTime::Format::Strptime;
139   $datetime_parser ||= DateTime::Format::Strptime->new(
140     pattern  => $datetime_format,
141     on_error => 'croak',
142   );
143   return $datetime_parser->parse_datetime(shift);
144 }
145
146 sub format_datetime {
147   shift;
148   require DateTime::Format::Strptime;
149   $datetime_parser ||= DateTime::Format::Strptime->new(
150     pattern  => $datetime_format,
151     on_error => 'croak',
152   );
153   return $datetime_parser->format_datetime(shift);
154 }
155
156 1;
157
158 =head1 AUTHOR
159
160 See L<DBIx::Class/AUTHOR> and L<DBIx::Class/CONTRIBUTORS>.
161
162 =head1 LICENSE
163
164 You may distribute this code under the same terms as Perl itself.
165
166 =cut
167 # vim:sts=2 sw=2: