/[fuse_dbi]/trunk/DBI.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/DBI.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/fuse_dbi.pl revision 2 by dpavlin, Wed Aug 4 09:03:05 2004 UTC trunk/DBI.pm revision 11 by dpavlin, Sun Aug 29 18:51:29 2004 UTC
# Line 1  Line 1 
1  #!/usr/bin/perl  #!/usr/bin/perl
2    
3  use POSIX qw(ENOENT EISDIR EINVAL);  package Fuse::DBI;
 use Fuse;  
4    
5  use DBI;  use 5.008;
6  use strict;  use strict;
7    use warnings;
8    
9  my $sql_filenames = q{  use POSIX qw(ENOENT EISDIR EINVAL ENOSYS O_RDWR);
10          select  use Fuse;
11                  templateid as id,  use DBI;
12                  namespace||'/'||name||' ['||templateid||']' as filename,  use Carp;
13                  length(template) as size,  use Proc::Simple;
14                  iseditable as writable  use Data::Dumper;
         from template ;  
 };  
15    
 my $sql_content = q{  
         select template  
         from template  
         where templateid = ?;  
 };  
16    
17    our $VERSION = '0.01';
18    
19    =head1 NAME
20    
21    Fuse::DBI - mount your database as filesystem and use it
22    
23    =head1 SYNOPSIS
24    
25      use Fuse::DBI;
26      Fuse::DBI->mount( ... );
27    
28    See L<run> below for examples how to set parametars.
29    
30    =head1 DESCRIPTION
31    
32  my $connect = "DBI:Pg:dbname=webgui";  This module will use L<Fuse> module, part of C<FUSE (Filesystem in USErspace)>
33    available at L<http://sourceforge.net/projects/avf> to mount
34    your database as file system.
35    
36  my $dbh = DBI->connect($connect,"","") || die $DBI::errstr;  That will give you posibility to use normal file-system tools (cat, grep, vi)
37    to manipulate data in database.
38    
39  print STDERR "$sql_filenames\n";  It's actually opposite of Oracle's intention to put everything into database.
40    
 my $sth_filenames = $dbh->prepare($sql_filenames) || die $dbh->errstr();  
 $sth_filenames->execute() || die $sth_filenames->errstr();  
41    
42  my $sth_content = $dbh->prepare($sql_content) || die $dbh->errstr();  =head1 METHODS
43    
44  print "#",join(",",@{ $sth_filenames->{NAME} }),"\n";  =cut
45    
46  my $ctime_start = time();  =head2 mount
47    
48    Mount your database as filesystem.
49    
50      my $mnt = Fuse::DBI->mount({
51            filenames => 'select name from filenamefilenames,
52            read => 'sql read',
53            update => 'sql update',
54            dsn => 'DBI:Pg:dbname=webgui',
55            user => 'database_user',
56            password => 'database_password'
57      });
58    
59    =cut
60    
61    my $dbh;
62    my $sth;
63    my $ctime_start;
64    
65    sub read_filenames;
66    
67    sub mount {
68            my $class = shift;
69            my $self = {};
70            bless($self, $class);
71    
72            my $arg = shift;
73    
74            print Dumper($arg);
75    
76            carp "mount needs 'dsn' to connect to (e.g. dsn => 'DBI:Pg:dbname=test')" unless ($arg->{'dsn'});
77            carp "mount needs 'mount' as mountpoint" unless ($arg->{'mount'});
78    
79            foreach (qw(filenames read update)) {
80                    carp "mount needs '$_' SQL" unless ($arg->{$_});
81            }
82    
83  my (%files) = (          $dbh = DBI->connect($arg->{'dsn'},$arg->{'user'},$arg->{'password'}, { AutoCommit => 0 }) || die $DBI::errstr;
         '.' => {  
                 type => 0040,  
                 mode => 0755,  
         },  
 #       a => {  
 #               cont => "File 'a'.\n",  
 #               type => 0100,  
 #               ctime => time()-2000  
 #       },  
 );  
84    
85            print "start transaction\n";
86            #$dbh->begin_work || die $dbh->errstr;
87    
88            $sth->{filenames} = $dbh->prepare($arg->{'filenames'}) || die $dbh->errstr();
89    
90            $sth->{'read'} = $dbh->prepare($arg->{'read'}) || die $dbh->errstr();
91            $sth->{'update'} = $dbh->prepare($arg->{'update'}) || die $dbh->errstr();
92    
93            $ctime_start = time();
94    
95            read_filenames;
96    
97            $self->{'proc'} = Proc::Simple->new();
98            $self->{'proc'}->kill_on_destroy(1);
99    
100            $self->{'proc'}->start( sub {
101                    Fuse::main(
102                            mountpoint=>$arg->{'mount'},
103                            getattr=>\&e_getattr,
104                            getdir=>\&e_getdir,
105                            open=>\&e_open,
106                            statfs=>\&e_statfs,
107                            read=>\&e_read,
108                            write=>\&e_write,
109                            utime=>\&e_utime,
110                            truncate=>\&e_truncate,
111                            debug=>0,
112                    );
113            } );
114    
115            $self ? return $self : return undef;
116    };
117    
118    =head2 umount
119    
120    Unmount your database as filesystem.
121    
122      $mnt->umount;
123    
124    This will also kill background process which is translating
125    database to filesystem.
126    
127    =cut
128    
129    sub umount {
130            my $self = shift;
131    
132            confess "no process running?" unless ($self->{'proc'});
133            $self->{'proc'}->kill;
134    }
135    
136    
137    my %files;
138  my %dirs;  my %dirs;
139    
140  while (my $row = $sth_filenames->fetchrow_hashref() ) {  sub read_filenames {
141          $files{$row->{'filename'}} = {          my $self = shift;
142                  size => $row->{'size'},  
143                  mode => $row->{'writable'} ? 0644 : 0444,          # create empty filesystem
144                  id => $row->{'id'} || 99,          (%files) = (
145          };                  '.' => {
146                            type => 0040,
147          my $d;                          mode => 0755,
148          foreach (split(m!/!, $row->{'filename'})) {                  },
149                  # first, entry is assumed to be file          #       a => {
150                  if ($d) {          #               cont => "File 'a'.\n",
151                          $files{$d} = {          #               type => 0100,
152                                          size => $dirs{$d}++,          #               ctime => time()-2000
153                                          mode => 0755,          #       },
154                                          type => 0040          );
155                          };  
156                          $files{$d.'/.'} = {          # fetch new filename list from database
157                                          mode => 0755,          $sth->{'filenames'}->execute() || die $sth->{'filenames'}->errstr();
158                                          type => 0040  
159                          };          # read them in with sesible defaults
160                          $files{$d.'/..'} = {          while (my $row = $sth->{'filenames'}->fetchrow_hashref() ) {
161                                          mode => 0755,                  $files{$row->{'filename'}} = {
162                                          type => 0040                          size => $row->{'size'},
163                          };                          mode => $row->{'writable'} ? 0644 : 0444,
164                            id => $row->{'id'} || 99,
165                    };
166    
167                    my $d;
168                    foreach (split(m!/!, $row->{'filename'})) {
169                            # first, entry is assumed to be file
170                            if ($d) {
171                                    $files{$d} = {
172                                                    size => $dirs{$d}++,
173                                                    mode => 0755,
174                                                    type => 0040
175                                    };
176                                    $files{$d.'/.'} = {
177                                                    mode => 0755,
178                                                    type => 0040
179                                    };
180                                    $files{$d.'/..'} = {
181                                                    mode => 0755,
182                                                    type => 0040
183                                    };
184                            }
185                            $d .= "/" if ($d);
186                            $d .= "$_";
187                  }                  }
                 $d .= "/" if ($d);  
                 $d .= "$_";  
188          }          }
189    
190            print "found ",scalar(keys %files)-scalar(keys %dirs)," files, ",scalar(keys %dirs), " dirs\n";
191  }  }
192    
 print scalar (keys %dirs), " dirs:",join(" ",keys %dirs),"\n";  
193    
194  sub filename_fixup {  sub filename_fixup {
195          my ($file) = shift;          my ($file) = shift;
# Line 123  sub e_getdir { Line 231  sub e_getdir {
231                  } else {                  } else {
232                          $out{$f}++ if ($f =~ /^[^\/]+$/);                          $out{$f}++ if ($f =~ /^[^\/]+$/);
233                  }                  }
                 print "f: $_ -> $f\n";  
234          }          }
235          if (! %out) {          if (! %out) {
236                  $out{'no files? bug?'}++;                  $out{'no files? bug?'}++;
237          }          }
238          print scalar keys %out," files found for '$dirname': ",keys %out,"\n";          print scalar keys %out," files in dir '$dirname'\n";
239          return (keys %out),0;          return (keys %out),0;
240  }  }
241    
242  sub e_open {  sub e_open {
243          # VFS sanity check; it keeps all the necessary state, not much to do here.          # VFS sanity check; it keeps all the necessary state, not much to do here.
244          my ($file) = filename_fixup(shift);          my $file = filename_fixup(shift);
245          print("open called\n");          my $flags = shift;
246    
247          return -ENOENT() unless exists($files{$file});          return -ENOENT() unless exists($files{$file});
248          return -EISDIR() unless exists($files{$file}{id});          return -EISDIR() unless exists($files{$file}{id});
249    
250          if (!exists($files{$file}{cont})) {          if (!exists($files{$file}{cont})) {
251                  $sth_content->execute($files{$file}{id});                  $sth->{'read'}->execute($files{$file}{id}) || die $sth->{'read'}->errstr;
252                  ($files{$file}{cont}) = $sth_content->fetchrow_array;                  $files{$file}{cont} = $sth->{'read'}->fetchrow_array;
253                    print "file '$file' content read in cache\n";
254          }          }
255          print("open ok\n");          print "open '$file' ",length($files{$file}{cont})," bytes\n";
256          return 0;          return 0;
257  }  }
258    
259  sub e_read {  sub e_read {
260          # return an error numeric, or binary/text string.  (note: 0 means EOF, "0" will          # return an error numeric, or binary/text string.
261          # give a byte (ascii "0") to the reading program)          # (note: 0 means EOF, "0" will give a byte (ascii "0")
262            # to the reading program)
263          my ($file) = filename_fixup(shift);          my ($file) = filename_fixup(shift);
264          my ($buf,$off) = @_;          my ($buf_len,$off) = @_;
265    
266          return -ENOENT() unless exists($files{$file});          return -ENOENT() unless exists($files{$file});
267          return -EINVAL() if $off > length($files{$file}{cont});  
268          return 0 if $off == length($files{$file}{cont});          my $len = length($files{$file}{cont});
269          return substr($files{$file}{cont},$off,$buf);  
270            print "read '$file' [$len bytes] offset $off length $buf_len\n";
271    
272            return -EINVAL() if ($off > $len);
273            return 0 if ($off == $len);
274    
275            $buf_len = $buf_len-$off if ($off+$buf_len > $len);
276    
277            return substr($files{$file}{cont},$off,$buf_len);
278    }
279    
280    sub clear_cont {
281            print "transaction rollback\n";
282            $dbh->rollback || die $dbh->errstr;
283            print "invalidate all cached content\n";
284            foreach my $f (keys %files) {
285                    delete $files{$f}{cont};
286            }
287            print "begin new transaction\n";
288            $dbh->begin_work || die $dbh->errstr;
289    }
290    
291    
292    sub update_db {
293            my $file = shift || die;
294    
295            $files{$file}{ctime} = time();
296    
297            if (!$sth->{'update'}->execute($files{$file}{cont},$files{$file}{id})) {
298                    print "update problem: ",$sth->{'update'}->errstr;
299                    clear_cont;
300                    return 0;
301            } else {
302                    if (! $dbh->commit) {
303                            print "ERROR: commit problem: ",$sth->{'update'}->errstr;
304                            clear_cont;
305                            return 0;
306                    }
307                    print "updated '$file' [",$files{$file}{id},"]\n";
308            }
309            return 1;
310    }
311    
312    sub e_write {
313            my $file = filename_fixup(shift);
314            my ($buf_len,$off) = @_;
315    
316            return -ENOENT() unless exists($files{$file});
317    
318            my $len = length($files{$file}{cont});
319    
320            print "write '$file' [$len bytes] offset $off length\n";
321    
322            $files{$file}{cont} =
323                    substr($files{$file}{cont},0,$off) .
324                    $buf_len .
325                    substr($files{$file}{cont},$off+length($buf_len));
326    
327            if (! update_db($file)) {
328                    return -ENOSYS();
329            } else {
330                    return length($buf_len);
331            }
332    }
333    
334    sub e_truncate {
335            my $file = filename_fixup(shift);
336            my $size = shift;
337    
338            $files{$file}{cont} = substr($files{$file}{cont},0,$size);
339            return 0
340    };
341    
342    
343    sub e_utime {
344            my ($atime,$mtime,$file) = @_;
345            $file = filename_fixup($file);
346    
347            return -ENOENT() unless exists($files{$file});
348    
349            print "utime '$file' $atime $mtime\n";
350    
351            $files{$file}{time} = $mtime;
352            return 0;
353  }  }
354    
355  sub e_statfs { return 255, 1, 1, 1, 1, 2 }  sub e_statfs { return 255, 1, 1, 1, 1, 2 }
356    
357  # If you run the script directly, it will run fusermount, which will in turn  1;
358  # re-run this script.  Hence the funky semantics.  __END__
359  my ($mountpoint) = "";  
360  $mountpoint = shift(@ARGV) if @ARGV;  =head1 EXPORT
361  Fuse::main(  
362          mountpoint=>$mountpoint,  Nothing.
363          getattr=>\&e_getattr,  
364          getdir=>\&e_getdir,  =head1 SEE ALSO
365          open=>\&e_open,  
366          statfs=>\&e_statfs,  C<FUSE (Filesystem in USErspace)> website
367          read=>\&e_read,  L<http://sourceforge.net/projects/avf>
368          debug=>1,  
369  );  =head1 AUTHOR
370    
371    Dobrica Pavlinusic, E<lt>dpavlin@rot13.orgE<gt>
372    
373    =head1 COPYRIGHT AND LICENSE
374    
375    Copyright (C) 2004 by Dobrica Pavlinusic
376    
377    This library is free software; you can redistribute it and/or modify
378    it under the same terms as Perl itself, either Perl version 5.8.4 or,
379    at your option, any later version of Perl 5 you may have available.
380    
381    
382    =cut
383    

Legend:
Removed from v.2  
changed lines
  Added in v.11

  ViewVC Help
Powered by ViewVC 1.1.26