File Coverage

blib/lib/DBD/Crate.pm
Criterion Covered Total %
statement 69 202 34.1
branch 0 54 0.0
condition 0 25 0.0
subroutine 23 41 56.1
pod 0 3 0.0
total 92 325 28.3


line stmt bran cond sub pod time code
1             package DBD::Crate;
2 5     5   42271 use strict;
  5         6  
  5         109  
3 5     5   2617 use DBI;
  5         82760  
  5         180  
4 5     5   3050 use HTTP::Tiny;
  5         169989  
  5         163  
5 5     5   1958 use JSON::MaybeXS;
  5         25200  
  5         265  
6 5     5   24 use vars qw($VERSION $REVISION);
  5         6  
  5         243  
7 5     5   20 use vars qw($err $errstr $state $drh);
  5         6  
  5         814  
8             $VERSION = "0.0.3";
9            
10             $err = 0;
11             $errstr = "";
12             $state = "";
13             $drh = undef;
14             my $methods_are_installed = 0;
15             my ($HTTP, $JSON);
16            
17             sub driver {
18 0 0   0 0   return $drh if $drh;
19 0           my ($class, $attr) = @_;
20 0           $class .= "::dr";
21 0 0         $drh = DBI::_new_drh($class, {
22             'Name' => 'Crate',
23             'Version' => $VERSION,
24             'Err' => \$err,
25             'Errstr' => \$errstr,
26             'State' => \$state,
27             'Attribution' => 'DBD::Crate by Mamod Mehyar',
28             'AutoCommit' => 1
29             }) or return undef;
30 0           return $drh;
31             }
32            
33 0     0 0   sub http { $HTTP }
34 0     0 0   sub json { $JSON }
35            
36             #====================================================================
37             # DBD::Crate::dr
38             #====================================================================
39             package DBD::Crate::dr; {
40 5     5   26 use strict;
  5         4  
  5         120  
41 5     5   17 use DBI qw(:sql_types);
  5         10  
  5         2056  
42 5     5   25 use vars qw($imp_data_size);
  5         5  
  5         147  
43 5     5   40 use Carp qw(carp croak);
  5         4  
  5         199  
44 5     5   2541 use Data::Dumper;
  5         20783  
  5         226  
45 5     5   22 use DBI;
  5         5  
  5         1232  
46            
47             $imp_data_size = 0;
48            
49             sub connect {
50 0     0     my ($drh, $dburl, $user, $pass, $attr) = @_;
51             my $UTF8 = defined $attr->{utf8} ?
52 0 0         $attr->{utf8} : 1;
53            
54 0           $JSON = JSON::MaybeXS->new({ utf8 => $UTF8 });
55 0           $HTTP = HTTP::Tiny->new( keep_alive => 1 );
56            
57 0           my @addresses = ($dburl);
58 0           my @addr;
59 0 0         if ($dburl =~ s/^\[(.*?)\]$/$1/){
60 0           @addresses = split ',', $dburl;
61             }
62            
63 0           foreach my $addr (@addresses){
64 0           $addr =~ s/\s+//;
65 0 0         if (!$addr){
66 0           $addr = 'http://localhost:4200';
67             }
68            
69 0 0         if ($addr !~ /^http/){
70 0           $addr = 'http://' . $addr;
71             }
72            
73 0 0 0       if ($user || $pass){
74 0   0       my $auth = ($user || '') . ':' . ($pass || '');
      0        
75 0           $addr =~ s/^(http(?:.)?:\/\/)(.*?)/$1$auth\@$2/;
76             }
77 0           push @addr, $addr;
78             }
79            
80 0           my ($t, $dbh) = DBI::_new_dbh($drh, {
81             'Name' => \@addr
82             });
83            
84 0           return $dbh;
85             }
86            
87 0     0     sub data_sources { return "Cratedb" }
88 0     0     sub disconnect_all { 1 }
89             };
90            
91             #====================================================================
92             # DBD::Crate::db
93             #====================================================================
94             package DBD::Crate::db; {
95 5     5   21 use strict;
  5         6  
  5         99  
96 5     5   21 use base qw(DBD::_::db);
  5         5  
  5         1273  
97 5     5   21 use vars qw($imp_data_size);
  5         7  
  5         146  
98 5     5   15 use Data::Dumper;
  5         5  
  5         190  
99 5     5   15 use DBI;
  5         6  
  5         132  
100 5     5   1882 use Digest::SHA1 qw(sha1_hex);
  5         2288  
  5         2687  
101            
102             $imp_data_size = 0;
103            
104             sub prepare {
105 0     0     my ($dbh, $statement, @attr) = @_;
106             my $sth = DBI::_new_sth($dbh, {
107             'Statement' => $statement,
108             'ConnectionHOST' => $dbh->{Name}
109 0           });
110 0           return $sth;
111             }
112            
113             #=============================================================
114             # blob methods
115             #=============================================================
116             sub crate_blob_insert {
117 0     0     my ($dbh, $table, $digest, $content) = @_;
118            
119 0 0         if (!$content){
120 0           $content = $digest;
121 0           $digest = sha1_hex($content);
122             }
123            
124 0           my $path = "/_blobs/$table/" . $digest;
125             my $sth = DBI::_new_sth($dbh, {
126             'REQUEST_PATH' => $path,
127             'REQUEST_METHOD' => 'PUT',
128             'Statement' => $content,
129             'ConnectionHOST' => $dbh->{Name},
130 0           'BLOB' => 1,
131             'DIGEST' => $digest
132             });
133            
134 0           return $sth->execute();
135             };
136            
137             sub crate_blob_get {
138 0     0     my ($dbh, $table, $digest) = @_;
139            
140 0 0         if (!$digest){
141 0           $dbh->set_err(-1, "BLOB sha1 digest required");
142 0           return;
143             }
144            
145 0   0       $digest ||= '';
146            
147 0           my $path = "/_blobs/$table/" . $digest;
148             my $sth = DBI::_new_sth($dbh, {
149             'REQUEST_PATH' => $path,
150             'REQUEST_METHOD' => 'GET',
151             'Statement' => '',
152             'ConnectionHOST' => $dbh->{Name},
153 0           'BLOB' => 1,
154             'DIGEST' => $digest
155             });
156 0           return $sth->execute();
157             }
158            
159             sub crate_blob_delete {
160 0     0     my ($dbh, $table, $digest) = @_;
161            
162 0 0         if (!$digest){
163 0           $dbh->set_err(-1, "BLOB sha1 digest required");
164 0           return;
165             }
166            
167 0           my $path = "/_blobs/$table/" . $digest;
168             my $sth = DBI::_new_sth($dbh, {
169             'REQUEST_PATH' => $path,
170             'REQUEST_METHOD' => 'DELETE',
171             'Statement' => '',
172             'ConnectionHOST' => $dbh->{Name},
173 0           'BLOB' => 1,
174             'DIGEST' => $digest
175             });
176            
177 0           return $sth->execute();
178             }
179            
180             #=============================================================
181             # table info methods
182             #=============================================================
183             #return columns information of provided table
184             sub crate_table_columns {
185 0     0     my ($dbh, $table) = @_;
186 0           my $sth = $dbh->prepare(qq~
187             select column_name, data_type, ordinal_position
188             from information_schema.columns
189             where schema_name = 'doc'
190             AND table_name = ?
191             ~);
192            
193 0           return $dbh->selectall_arrayref( $sth,
194             { Slice => {} },
195             $table);
196             }
197            
198             #list all tables with information
199             sub crate_tables_list {
200 0     0     my $dbh = shift;
201 0   0       my $schema = shift || 'doc';
202 0           my $sth = $dbh->prepare(qq~
203             select number_of_replicas, partitioned_by, blobs_path, schema_name,
204             table_name, number_of_shards, clustered_by
205             from information_schema.tables
206             where schema_name = ?
207             ~);
208            
209 0           return $dbh->selectall_arrayref( $sth,
210             { Slice => {} },
211             $schema);
212             }
213            
214             #get table info
215             sub crate_table_info {
216 0     0     my $dbh = shift;
217 0           my $table = shift;
218 0           my $sth = $dbh->prepare(qq~
219             select number_of_replicas, partitioned_by, blobs_path, schema_name,
220             table_name, number_of_shards, clustered_by
221             from information_schema.tables
222             where schema_name = 'doc'
223             AND table_name = ?
224             ~);
225            
226 0           return $dbh->selectrow_hashref( $sth,
227             undef,
228             $table);
229             }
230            
231             #==================================================
232             # These should be removed once we get crate name
233             # space registered with DBI
234             #==================================================
235             *DBI::db::crate_blob_insert = \&crate_blob_insert;
236             *DBI::db::crate_blob_get = \&crate_blob_get;
237             *DBI::db::crate_blob_delete = \&crate_blob_delete;
238             *DBI::db::crate_table_columns = \&crate_table_columns;
239             *DBI::db::crate_tables_list = \&crate_tables_list;
240             *DBI::db::crate_table_info = \&crate_table_info;
241             };
242            
243             #====================================================================
244             # DBD::Crate::st
245             #====================================================================
246             package DBD::Crate::st; {
247 5     5   24 use strict;
  5         5  
  5         98  
248 5     5   14 use base qw(DBD::_::st);
  5         16  
  5         1114  
249 5     5   17 use vars qw($imp_data_size);
  5         5  
  5         135  
250 5     5   16 use Data::Dumper;
  5         5  
  5         165  
251 5     5   17 use DBI;
  5         5  
  5         2786  
252            
253             $imp_data_size = 0;
254            
255             sub _fetch_data {
256 0     0     my $sth = shift;
257 0           my $statement = shift;
258 0   0       my $method = shift || 'POST';
259 0   0       my $path = shift || '/_sql';
260            
261 0           my @hosts = @{ $sth->{ConnectionHOST} };
  0            
262            
263 0           TRYAGAIN :
264             my $host = shift @hosts;
265 0           my $ret = DBD::Crate::http->request($method, $host . $path, {
266             content => $statement,
267             # headers => {'Content-Type' => 'application/json'}
268             });
269            
270 0 0 0       if ( $ret->{status} == 599 && scalar @hosts){
271 0           my $i = 0;
272 0           for (@{ $sth->{ConnectionHOST} }){
  0            
273             #put failing address to the end of hosts
274             #this will work only on persistant environments
275 0 0         if ( $host eq $_ ){
276 0           splice @{ $sth->{ConnectionHOST} }, $i, 1;
  0            
277 0           push @{ $sth->{ConnectionHOST} }, $_;
  0            
278             }
279 0           $i++;
280             }
281 0           goto TRYAGAIN;
282             }
283            
284 0 0         if (!$ret->{success}){
285 0           my $olderr = $@;
286 0           my $data = eval { DBD::Crate::json->decode($ret->{content}) };
  0            
287 0           $@ = $olderr;
288            
289             my $error = ref $data eq 'HASH' && ref $data->{error} ? $data->{error} : {
290             code => ref $data ? $data->{status} : $ret->{status},
291             message => ref $data ? $data->{error} : ($ret->{content} || $ret->{reason})
292 0 0 0       };
    0 0        
    0          
293            
294 0           $sth->set_err($error->{code}, $error->{message});
295 0           return;
296             }
297            
298 0           return $ret;
299             }
300            
301             sub execute {
302 0     0     my $sth = shift;
303 0           my $statement = $sth->{Statement};
304 0           my $ret;
305 0 0         if ($sth->{BLOB}){
306 0 0         if (!$sth->{DIGEST}){
307 0           $sth->set_err(-1, "BLOB sha1 digest required");
308 0           return;
309             }
310            
311             $ret = _fetch_data($sth, $statement,
312             $sth->{REQUEST_METHOD},
313 0 0         $sth->{REQUEST_PATH}) or return;
314             } else {
315 0           my $hash = {stmt => $statement };
316 0 0         if (@_){ $hash->{args} = \@_; }
  0            
317 0           my $json = DBD::Crate::json->encode($hash);
318 0 0         $ret = _fetch_data($sth, $json, 'POST', '/_sql') or return;
319             }
320            
321 0           $sth->{'driver_raw_data'} = $ret->{content};
322 0 0         if ($sth->{BLOB}){
323 0 0         if ($ret->{status} == 201){ # put success
    0          
    0          
324 0           return $sth->{DIGEST};
325             } elsif ($ret->{status} == 200){ #get success
326 0           return $ret->{content};
327             } elsif ($ret->{status} == 204){ #delete success
328 0           return 1;
329             }
330             }
331            
332 0           my $olderr = $@;
333 0           my $data = eval { DBD::Crate::json->decode($ret->{content}) };
  0            
334 0 0         if (!$data){
335 0           my $error = $@;
336 0           $@ = $olderr;
337 0           $sth->set_err(-1, $error);
338 0           return;
339             }
340            
341            
342 0           $sth->{'driver_data'} = $data->{rows};
343 0           $sth->{'driver_rows'} = $data->{rowcount};
344            
345 0           $sth->{'NAME'} = $data->{cols};
346 0           $sth->STORE('NUM_OF_FIELDS', scalar @{ $data->{cols} });
  0            
347 0   0       return $data->{rowcount} || '0E0';
348             }
349            
350             *fetch = \&fetchrow_arrayref;
351             sub fetchrow_arrayref {
352 0     0     my $sth = shift;
353 0           my $data = $sth->FETCH('driver_data');
354 0 0         my $row = shift @$data or return;
355 0           return $sth->_set_fbav($row);
356             }
357            
358             sub raw {
359 0     0     my $sth = shift;
360 0           my $data = $sth->FETCH('driver_raw_data');
361 0           return $data;
362             }
363            
364             *DBI::st::raw = \&raw;
365            
366             #Nothing to close, crate is stateless
367       0     sub close {}
368             };
369            
370            
371             1;
372            
373             __END__