0ce1b16e3614018974d517436b1a5e01db8bb72f
[freeside.git] / FS / FS / part_export / domain_sql.pm
1 package FS::part_export::domain_sql;
2
3 use vars qw(@ISA %info);
4 use Tie::IxHash;
5 use FS::part_export;
6
7 @ISA = qw(FS::part_export);
8
9 #quite a bit of false laziness w/acct_sql - some stuff should be generalized
10 #out to a "dababase base class"
11
12 tie my %options, 'Tie::IxHash',
13   'datasrc'            => { label => 'DBI data source' },
14   'username'           => { label => 'Database username' },
15   'password'           => { label => 'Database password' },
16   'table'              => { label => 'Database table' },
17   'schema'             => { label =>
18                               'Database schema mapping to Freeside methods.',
19                             type  => 'textarea',
20                           },
21   'static'             => { label =>
22                               'Database schema mapping to static values.',
23                             type  => 'textarea',
24                           },
25   'primary_key'        => { label => 'Database primary key' },
26 ;
27
28 tie my %postfix_transport_map, 'Tie::IxHash', 
29   'domain' => 'domain'
30 ;
31 my $postfix_transport_map = 
32   join('\n', map "$_ $postfix_transport_map{$_}",
33                  keys %postfix_transport_map      );
34 tie my %postfix_transport_static, 'Tie::IxHash',
35   'transport' => 'virtual:',
36 ;
37 my $postfix_transport_static = 
38   join('\n', map "$_ $postfix_transport_static{$_}",
39                  keys %postfix_transport_static      );
40
41 %info  = (
42   'svc'     => 'svc_domain',
43   'desc'    => 'Real time export of domains to SQL databases '.
44                '(postfix, others?)',
45   'options' => \%options,
46   'notes'   => <<END
47 Export domains (svc_domain records) to SQL databases.  Currently this is a
48 simple export with a default for Postfix, but it can be extended for other
49 uses.
50
51 <BR><BR>Use these buttons for useful presets:
52 <UL>
53   <LI><INPUT TYPE="button" VALUE="postfix_transport" onClick='
54     this.form.table.value = "transport";
55     this.form.schema.value = "$postfix_transport_map";
56     this.form.static.value = "$postfix_transport_static";
57     this.form.primary_key.value = "domain";
58   '>
59 </UL>
60 END
61 );
62
63 sub _schema_map { shift->_map('schema'); }
64 sub _static_map { shift->_map('static'); }
65
66 sub _map {
67   my $self = shift;
68   map { /^\s*(\S+)\s*(\S+)\s*$/ } split("\n", $self->option(shift) );
69 }
70
71 sub _export_insert {
72   my($self, $svc_domain) = (shift, shift);
73
74   my %schema = $self->_schema_map;
75   my %static = $self->_static_map;
76
77   my %record = ( ( map { $_ => $static{$_}       } keys %static ),
78                  ( map { my $method = $schema{$_};
79                        $_ => $svc_domain->$method();
80                        }
81                        keys %schema
82                  )
83                );
84
85   my $err_or_queue = 
86     $self->domain_sql_queue(
87       $svc_domain->svcnum,
88       'insert',
89       $self->option('table'),
90       %record
91     );
92   return $err_or_queue unless ref($err_or_queue);
93
94   '';
95 }
96
97 sub _export_replace {
98   my($self, $new, $old) = (shift, shift, shift);
99
100   my %schema = $self->_schema_map;
101   my %static = $self->_static_map;
102
103   my @primary_key = ();
104   if ( $self->option('primary_key') =~ /,/ ) {
105     foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
106       my $keymap = $schema{$key};
107       push @primary_key, $old->$keymap();
108     }
109   } else {
110     my $keymap = $map{$self->option('primary_key')};
111     push @primary_key, $old->$keymap();
112   }
113
114   my %record = ( ( map { $_ => $static{$_}       } keys %static ),
115                  ( map { my $method = $schema{$_};
116                          $_ => $new->$method();
117                        }
118                        keys %schema
119                  )
120                );
121
122   my $err_or_queue = $self->domain_sql_queue(
123     $new->svcnum,
124     'replace',
125     $self->option('table'),
126     $self->option('primary_key'), @primary_key, 
127     %record,
128   );
129   return $err_or_queue unless ref($err_or_queue);
130   '';
131 }
132
133 sub _export_delete {
134   my ( $self, $svc_domain ) = (shift, shift);
135
136   my %schema = $self->_schema_map;
137   my %static = $self->_static_map;
138
139   my %primary_key = ();
140   if ( $self->option('primary_key') =~ /,/ ) {
141     foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
142       my $keymap = $map{$key};
143       $primary_key{ $key } = $svc_domain->$keymap();
144     }
145   } else {
146     my $keymap = $map{$self->option('primary_key')};
147     $primary_key{ $self->option('primary_key') } = $svc_domain->$keymap(),
148   }
149
150   my $err_or_queue = $self->domain_sql_queue(
151     $svc_domain->svcnum,
152     'delete',
153     $self->option('table'),
154     %primary_key,
155     #$self->option('primary_key') => $svc_domain->$keymap(),
156   );
157   return $err_or_queue unless ref($err_or_queue);
158   '';
159 }
160
161 sub domain_sql_queue {
162   my( $self, $svcnum, $method ) = (shift, shift, shift);
163   my $queue = new FS::queue {
164     'svcnum' => $svcnum,
165     'job'    => "FS::part_export::domain_sql::domain_sql_$method",
166   };
167   $queue->insert(
168     $self->option('datasrc'),
169     $self->option('username'),
170     $self->option('password'),
171     @_,
172   ) or $queue;
173 }
174
175 sub domain_sql_insert { #subroutine, not method
176   my $dbh = domain_sql_connect(shift, shift, shift);
177   my( $table, %record ) = @_;
178
179   my $sth = $dbh->prepare(
180     "INSERT INTO $table ( ". join(", ", keys %record).
181     " ) VALUES ( ". join(", ", map '?', keys %record ). " )"
182   ) or die $dbh->errstr;
183
184   $sth->execute( values(%record) )
185     or die "can't insert into $table table: ". $sth->errstr;
186
187   $dbh->disconnect;
188 }
189
190 sub domain_sql_delete { #subroutine, not method
191   my $dbh = domain_sql_connect(shift, shift, shift);
192   my( $table, %record ) = @_;
193
194   my $sth = $dbh->prepare(
195     "DELETE FROM $table WHERE ". join(' AND ', map "$_ = ? ", keys %record )
196   ) or die $dbh->errstr;
197
198   $sth->execute( map $record{$_}, keys %record )
199     or die "can't delete from $table table: ". $sth->errstr;
200
201   $dbh->disconnect;
202 }
203
204 sub domain_sql_replace { #subroutine, not method
205   my $dbh = domain_sql_connect(shift, shift, shift);
206
207   my( $table, $pkey ) = ( shift, shift );
208
209   my %primary_key = ();
210   if ( $pkey =~ /,/ ) {
211     foreach my $key ( split(/\s*,\s*/, $pkey ) ) {
212       $primary_key{$key} = shift;
213     }
214   } else {
215     $primary_key{$pkey} = shift;
216   }
217
218   my %record = @_;
219
220   my $sth = $dbh->prepare(
221     "UPDATE $table".
222     ' SET '.   join(', ',    map "$_ = ?", keys %record      ).
223     ' WHERE '. join(' AND ', map "$_ = ?", keys %primary_key )
224   ) or die $dbh->errstr;
225
226   $sth->execute( values(%record), values(%primary_key) );
227
228   $dbh->disconnect;
229 }
230
231 sub domain_sql_connect {
232   #my($datasrc, $username, $password) = @_;
233   #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
234   DBI->connect(@_) or die $DBI::errstr;
235 }
236
237 1;
238