File Coverage

blib/lib/Acrux/DBI/Dump.pm
Criterion Covered Total %
statement 18 74 24.3
branch 0 26 0.0
condition 0 52 0.0
subroutine 6 11 54.5
pod 5 5 100.0
total 29 168 17.2


line stmt bran cond sub pod time code
1             package Acrux::DBI::Dump;
2 5     5   31 use strict;
  5         10  
  5         192  
3 5     5   23 use utf8;
  5         10  
  5         27  
4              
5             =encoding utf8
6              
7             =head1 NAME
8              
9             Acrux::DBI::Dump - Working with SQL dumps
10              
11             =head1 SYNOPSIS
12              
13             use Acrux::DBI::Dump;
14              
15             my $dump = Acrux::DBI::Dump->new(
16             dbi => $dbi
17             );
18              
19             $dump->from_file('/tmp/test.sql')->poke;
20              
21             =head1 DESCRIPTION
22              
23             This class is used by L to allow database schemas import.
24             A dump file is just a collection of sql blocks, with one or more statements, separated by comments of the form
25             C<-- #NAME> or C<-- # NAME>
26              
27             -- #foo
28             CREATE TABLE `pets` (`pet` TEXT);
29             INSERT INTO `pets` VALUES ('cat');
30             INSERT INTO `pets` VALUES ('dog');
31             delimiter //
32             CREATE PROCEDURE `test`()
33             BEGIN
34             SELECT `pet` FROM `pets`;
35             END
36             //
37              
38             -- #bar
39             DROP TABLE `pets`;
40             DROP PROCEDURE `test`;
41              
42             -- #baz (...you can comment freely here...)
43             -- ...and here...
44             CREATE TABLE `stuff` (`whatever` INT);
45              
46             -- #main
47             DROP TABLE `stuff`;
48              
49             This idea is to let you import SQL dumps step by step by its names
50              
51             =head1 ATTRIBUTES
52              
53             This class implements the following attributes
54              
55             =head2 dbi
56              
57             $dump = $dump->dbi($dbi);
58             my $dbi = $dump->dbi;
59              
60             The object these processing belong to
61              
62             =head2 name
63              
64             my $name = $dump->name;
65             $dump = $dump->name('foo');
66              
67             Name for this dump, defaults to C
68              
69             =head1 METHODS
70              
71             This class implements all methods from L and implements
72             the following new ones
73              
74             =head2 from_data
75              
76             $dump = $dump->from_data;
77             $dump = $dump->from_data('main');
78             $dump = $dump->from_data('main', 'file_name');
79              
80             Extract dump data from a file in the DATA section of a class with
81             L, defaults to using the caller class and
82             L.
83              
84             __DATA__
85             @@ schema
86              
87             -- # up
88             CREATE TABLE `pets` (`pet` TEXT);
89             INSERT INTO `pets` VALUES ('cat');
90             INSERT INTO `pets` VALUES ('dog');
91              
92             -- # down
93             DROP TABLE `pets`
94              
95             =head2 from_file
96              
97             $dump = $dump->from_file('/tmp/schema.sql');
98              
99             Read dump data from a file
100              
101             =head2 from_string
102              
103             $dump = $dump->from_string('
104             -- # up
105             CREATE TABLE `pets` (`pet` TEXT);
106              
107             -- # down
108             DROP TABLE `pets`
109             ');
110              
111             Read dump data from string
112              
113             =head2 peek
114              
115             my $sqls = $dump->peek; # 'main'
116             my $sqls = $dump->peek('foo');
117             my @sqls = $dump->peek('foo');
118              
119             This method returns an array/arrayref of SQL statements stored at a specified dump location by tag-name.
120             By default will be used the C
tag
121              
122             =head2 poke
123              
124             $dump = $dump->poke; # 'main'
125             $dump = $dump->poke('foo');
126              
127             Import named data-block of SQL dump to database by tag-name. By default will be used the C
tag
128              
129             =head1 HISTORY
130              
131             See C file
132              
133             =head1 TO DO
134              
135             See C file
136              
137             =head1 SEE ALSO
138              
139             L, L, L
140              
141             =head1 AUTHOR
142              
143             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
144              
145             =head1 COPYRIGHT
146              
147             Copyright (C) 1998-2026 D&D Corporation
148              
149             =head1 LICENSE
150              
151             This program is distributed under the terms of the Artistic License Version 2.0
152              
153             See the C file or L for details
154              
155             =cut
156              
157 5     5   452 use Mojo::Base -base;
  5         10  
  5         62  
158              
159 5     5   4729 use Mojo::Loader qw/data_section/;
  5         198267  
  5         464  
160 5     5   53 use Mojo::File qw/path/;
  5         10  
  5         263  
161              
162             use constant {
163 5         8206 DELIMITER => ';',
164             TAG_DEFAULT => 'main',
165 5     5   29 };
  5         9  
166              
167             has name => 'schema';
168             has 'dbi';
169             has 'pool' => sub {{}};
170              
171             sub from_string {
172 0     0 1   my $self = shift;
173 0           my $s = shift;
174 0 0         return $self unless defined $s;
175 0           my $pool = $self->{pool} = {};
176 0           my $tag = TAG_DEFAULT;
177 0           my $delimiter = DELIMITER;
178 0           my $is_new = 1;
179 0           my $buf = '';
180              
181             # String processing
182 0           while (length($s)) {
183 0           my $chunk;
184              
185             # get fragments (chunks) from string
186 0 0 0       if ($s =~ /^$delimiter/x) { # any delimiter char(s)
    0 0        
    0 0        
    0 0        
      0        
      0        
187 0           $is_new = 1;
188 0           $chunk = $delimiter;
189             } elsif ($s =~ /^delimiter\s+(\S+)\s*(?:\n|\z)/ip) { # set new delimiter
190 0           $is_new = 1;
191 0           $chunk = ${^MATCH};
192 0           $delimiter = $1;
193             } elsif ($s =~ /^(\s+)/s or $s =~ /^(\w+)/) { # whitespaces or general name
194 0           $chunk = $1;
195             } elsif ($s =~ /^--.*(?:\n|\z)/p # double-dash comment
196             or $s =~ /^\#.*(?:\n|\z)/p # hash comment
197             or $s =~ /^\/\*(?:[^\*]|\*[^\/])*(?:\*\/|\*\z|\z)/p # C-style comment
198             or $s =~ /^'(?:[^'\\]*|\\(?:.|\n)|'')*(?:'|\z)/p # single-quoted literal text
199             or $s =~ /^"(?:[^"\\]*|\\(?:.|\n)|"")*(?:"|\z)/p # double-quoted literal text
200             or $s =~ /^`(?:[^`]*|``)*(?:`|\z)/p ) { # schema-quoted literal text
201 0           $chunk = ${^MATCH};
202             } else {
203 0           $chunk = substr($s, 0, 1);
204             }
205             #say STDERR ">$chunk<";
206              
207             # cut string by chunk length
208 0           substr($s, 0, length($chunk), '');
209              
210             # marker
211 0 0         if ($chunk =~ /^--\s+[#]+\s*(\w+)/i) {
212 0   0       my $_tag = $1 // TAG_DEFAULT;
213 0 0 0       push @{$pool->{$tag} //= []}, $buf if length($tag) and $buf !~ /^\s*$/s;
  0   0        
214 0           $tag = $_tag;
215 0           $is_new = 0;
216 0           $buf = '';
217 0           $delimiter = DELIMITER; # flush delimiter to default
218             }
219              
220             # make new block
221 0 0         if ($is_new) {
222 0 0 0       push @{$pool->{$tag} //= []}, $buf if length($tag) and $buf !~ /^\s*$/s;
  0   0        
223 0           $is_new = 0;
224 0           $buf = '';
225             } else { # Or add cur chunk to section
226 0           $buf .= $chunk;
227             }
228             }
229              
230             # add buf line to block
231 0 0 0       push @{$pool->{$tag} //= []}, $buf if length($tag) and $buf !~ /^\s*$/s;
  0   0        
232              
233 0           return $self;
234             }
235             sub from_data {
236 0     0 1   my $self = shift;
237 0           my $class = shift;
238 0           my $name = shift;
239 0   0       return $self->from_string(data_section($class //= caller, $name // $self->name));
      0        
240             }
241             sub from_file {
242 0     0 1   my $self = shift;
243 0           my $file = shift;
244 0           return $self->from_string(path($file)->slurp('UTF-8'));
245             }
246             sub poke {
247 0     0 1   my $self = shift;
248 0   0       my $tag = shift || TAG_DEFAULT;
249 0   0       my $sqls = $self->pool->{$tag} || [];
250 0           my $dbi = $self->dbi;
251 0 0 0       return $self unless $dbi and $dbi->ping;
252              
253             # Import statements
254 0           foreach my $sql (@$sqls) {
255             #print STDERR $sql, "\n";
256 0 0         $dbi->query($sql) or last;
257             }
258              
259 0           return $self;
260             }
261             sub peek {
262 0     0 1   my $self = shift;
263 0   0       my $tag = shift || TAG_DEFAULT;
264 0   0       my $sqls = $self->pool->{$tag} || [];
265 0 0         return wantarray ? (@$sqls) : [@$sqls]; # copy of data
266             }
267              
268             1;
269              
270             __END__