File Coverage

blib/lib/Metabase/Backend/SQL.pm
Criterion Covered Total %
statement 58 60 96.6
branch 3 6 50.0
condition n/a
subroutine 11 12 91.6
pod n/a
total 72 78 92.3


line stmt bran cond sub pod time code
1 9     9   5469 use 5.008001;
  9         28  
2 9     9   44 use strict;
  9         12  
  9         202  
3 9     9   36 use warnings;
  9         8  
  9         521  
4              
5             package Metabase::Backend::SQL;
6             # ABSTRACT: Metabase backend role for SQL-based backends
7              
8             our $VERSION = '1.001';
9              
10 9     9   41 use Class::Load qw/load_class try_load_class/;
  9         9  
  9         572  
11 9     9   3375 use SQL::Translator::Schema;
  9         1855645  
  9         366  
12 9     9   6175 use Storable qw/nfreeze/;
  9         26211  
  9         752  
13              
14 9     9   78 use Moose::Role;
  9         14  
  9         114  
15              
16             has [qw/dsn db_user db_pass db_type/] => (
17             is => 'ro',
18             isa => 'Str',
19             lazy_build => 1,
20             );
21              
22             has dbis => (
23             is => 'ro',
24             isa => 'DBIx::Simple',
25             lazy_build => 1,
26             handles => [qw/dbh/],
27             );
28              
29             has schema => (
30             is => 'ro',
31             isa => 'SQL::Translator::Schema',
32             lazy_build => 1,
33             );
34              
35             has '_blob_field_params' => (
36             is => 'ro',
37             isa => 'HashRef',
38             lazy_build => 1,
39             );
40              
41             has '_guid_field_params' => (
42             is => 'ro',
43             isa => 'HashRef',
44             lazy_build => 1,
45             );
46              
47             #--------------------------------------------------------------------------#
48             # to be implemented by Metabase::Backend::${DBNAME}
49             #--------------------------------------------------------------------------#
50              
51             requires '_build_dsn';
52             requires '_build_db_user';
53             requires '_build_db_pass';
54             requires '_build_db_type';
55             requires '_fixup_sql_diff';
56             requires '_build__blob_field_params';
57             requires '_build__guid_field_params';
58             requires '_munge_guid';
59             requires '_unmunge_guid';
60              
61              
62             #--------------------------------------------------------------------------#
63              
64             sub _build_dbis {
65 239     239   390 my ($self) = @_;
66 239         444 my @connect = map { $self->$_ } qw/dsn db_user db_pass/;
  717         24157  
67 239         2442 my $dbis = eval { DBIx::Simple->connect(@connect, {PrintWarn => 0}) }
68 239 50       554 or die "Could not connect via " . join(":",map { qq{'$_'} } @connect[0,1],"...")
  0         0  
69             . " because: $@\n";
70 239         228806 return $dbis;
71             }
72              
73             sub _build_schema {
74 79     79   160 my $self = shift;
75 79         2648 return SQL::Translator::Schema->new(
76             name => 'Metabase',
77             database => $self->db_type,
78             );
79             }
80              
81             sub _deploy_schema {
82 80     80   222 my ($self) = @_;
83              
84 80         2984 my $schema = $self->schema;
85              
86             # Blow up if this doesn't seem OK
87 80 50       496 $schema->is_valid or die "Could not validate schema: $schema->error";
88             # use Data::Dumper;
89             # warn "Schema: " . Dumper($schema);
90              
91 80         1017595 my $db_type = $self->db_type;
92             # See what we already have
93 80         734 my $existing = SQL::Translator->new(
94             parser => 'DBI',
95             parser_args => {
96             dbh => $self->dbh,
97             },
98             producer => $db_type,
99             show_warnings => 0, # suppress warning from empty DB
100             );
101             {
102             # shut up P::RD when there is no text -- the SQL::Translator parser
103             # forces things on when loaded. Gross.
104 9     9   50910 no warnings 'once';
  9         57  
  9         3126  
  80         212692  
105 80         561 load_class( "SQL::Translator::Parser::" . $db_type );
106 80         12777 load_class( "SQL::Translator::Producer::" . $db_type );
107 80         1774 local *main::RD_ERRORS;
108 80         181 local *main::RD_WARN;
109 80         146 local *main::RD_HINT;
110 80         416 my $existing_sql = $existing->translate();
111             # warn "*** Existing schema: " . $existing_sql;
112             }
113              
114             # Convert our target schema
115 80         25788549 my $fake = SQL::Translator->new(
116             parser => 'Storable',
117             producer => $db_type,
118             );
119 80         92160 my $fake_sql = $fake->translate( \( nfreeze($schema) ) );
120             # warn "*** Fake schema: $fake_sql";
121              
122 80         3065223 my $diff = SQL::Translator::Diff::schema_diff(
123             $existing->schema, $db_type, $fake->schema, $db_type
124             );
125              
126 80         2333129 $diff = $self->_fixup_sql_diff($diff);
127              
128             # DBIx::RunSQL requires a file (ugh)
129 80         563 my ($fh, $sqlfile) = File::Temp::tempfile();
130 80         338687 print {$fh} $diff;
  80         1329  
131 80         9962 close $fh;
132             # warn "*** Schema Diff:\n$diff\n"; # XXX
133              
134 80         3978 $self->clear_dbis; # ensure we re-initailize handle
135 80 50       22030 unless ( $diff =~ /-- No differences found/i ) {
136             DBIx::RunSQL->create(
137             dbh => $self->dbh,
138             sql => $sqlfile,
139 0     0   0 verbose_handler => sub { return },
140 80         696 );
141 80         162145 $self->dbh->disconnect;
142             }
143              
144             # must reset the connection
145 80         9518 $self->clear_dbis;
146 80         14324 $self->dbis; # rebuild
147              
148             # my ($count) = $self->dbis->query(qq{select count(*) from "core"})->list;
149             # warn "Initialized with $count records";
150 80         22310 return;
151             }
152              
153             1;
154              
155              
156             # vim: ts=2 sts=2 sw=2 et:
157              
158             __END__
159              
160             =pod
161              
162             =encoding UTF-8
163              
164             =head1 NAME
165              
166             Metabase::Backend::SQL - Metabase backend role for SQL-based backends
167              
168             =head1 VERSION
169              
170             version 1.001
171              
172             =head1 SYNOPSIS
173              
174             # SQLite
175              
176             require Metabase::Archive::SQLite;
177             require Metabase::Index::SQLite;
178              
179             my $archive = Metabase::Archive::SQLite->new(
180             filename => $sqlite_file,
181             );
182              
183             my $index = Metabase::Index::SQLite->new(
184             filename => $sqlite_file,
185             );
186              
187             # PostgreSQL
188              
189             use Metabase::Archive::PostgreSQL;
190             use Metabase::Index::PostgreSQL;
191              
192             my $archive = Metabase::Archive::PostgreSQL->new(
193             db_name => "cpantesters",
194             db_user => "johndoe",
195             db_pass => "PaSsWoRd",
196             );
197              
198             my $index = Metabase::Index::PostgreSQL->new(
199             db_name => "cpantesters",
200             db_user => "johndoe",
201             db_pass => "PaSsWoRd",
202             );
203              
204             =head1 DESCRIPTION
205              
206             This distribution contains implementations of L<Metabase::Archive> and
207             L<Metabase::Index> using SQL databases. >See L<Metabase::Backend::SQLite> or
208             L<Metabase::Backend::PostgreSQL> for details about specific implementations.
209              
210             The main module, itself, is merely a Moose role that provides common attributes
211             for all the SQL-based Metabase backends. It is not intended to be used
212             directly by end-users.
213              
214             =head1 ATTRIBUTES
215              
216             =head2 dsn
217              
218             Database connection string
219              
220             =head2 db_user
221              
222             Database username
223              
224             =head2 db_pass
225              
226             Database password
227              
228             =head2 db_type
229              
230             SQL::Translator sub-type for a given database. E.g. "SQLite" or "PostgreSQL".
231              
232             =head2 dbis
233              
234             DBIx::Simple class connected to the database
235              
236             =head2 schema
237              
238             SQL::Translator::Schema class
239              
240             =for Pod::Coverage method_names_here
241              
242             =head1 REQUIRED METHODS
243              
244             The following builders must be provided by consuming classes.
245              
246             _build_dsn # a DSN string for DBI
247             _build_db_user # a username for DBI
248             _build_db_pass # a password for DBI
249             _build_db_type # a SQL::Translator type for the DB vendor
250              
251             The following method must be provided to modify the output of
252             SQL::Translator::Diff to fix up any dialect quirks
253              
254             _fixup_sql_diff
255              
256             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
257              
258             =head1 SUPPORT
259              
260             =head2 Bugs / Feature Requests
261              
262             Please report any bugs or feature requests through the issue tracker
263             at L<https://github.com/dagolden/metabase-backend-sql/issues>.
264             You will be notified automatically of any progress on your issue.
265              
266             =head2 Source Code
267              
268             This is open source software. The code repository is available for
269             public review and contribution under the terms of the license.
270              
271             L<https://github.com/dagolden/metabase-backend-sql>
272              
273             git clone https://github.com/dagolden/metabase-backend-sql.git
274              
275             =head1 AUTHORS
276              
277             =over 4
278              
279             =item *
280              
281             David Golden <dagolden@cpan.org>
282              
283             =item *
284              
285             Leon Brocard <acme@astray.org>
286              
287             =back
288              
289             =head1 COPYRIGHT AND LICENSE
290              
291             This software is Copyright (c) 2011 by David Golden.
292              
293             This is free software, licensed under:
294              
295             The Apache License, Version 2.0, January 2004
296              
297             =cut