fix mapping problem in domain_sql export
[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   my $err_or_queue = 
84     $self->domain_sql_queue(
85       $svc_domain->svcnum,
86       'insert',
87       $self->option('table'),
88       %record
89     );
90   return $err_or_queue unless ref($err_or_queue);
91
92   '';
93 }
94
95 sub _export_replace {
96   my($self, $new, $old) = (shift, shift, shift);
97
98   my %schema = $self->_schema_map;
99   my %static = $self->_static_map;
100
101   my @primary_key = ();
102   if ( $self->option('primary_key') =~ /,/ ) {
103     foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
104       my $keymap = $schema{$key};
105       push @primary_key, $old->$keymap();
106     }
107   } else {
108     my $keymap = $map{$self->option('primary_key')};
109     push @primary_key, $old->$keymap();
110   }
111
112   my %record = ( map { $_ => $static{$_}       } keys %static ),
113                ( map { my $method = $schema{$_};
114                        $_ => $new->$method();
115                      }
116                      keys %schema );
117
118   my $err_or_queue = $self->domain_sql_queue(
119     $new->svcnum,
120     'replace',
121     $self->option('table'),
122     $self->option('primary_key'), @primary_key, 
123     %record,
124   );
125   return $err_or_queue unless ref($err_or_queue);
126   '';
127 }
128
129 sub _export_delete {
130   my ( $self, $svc_domain ) = (shift, shift);
131
132   my %schema = $self->_schema_map;
133   my %static = $self->_static_map;
134
135   my %primary_key = ();
136   if ( $self->option('primary_key') =~ /,/ ) {
137     foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
138       my $keymap = $map{$key};
139       $primary_key{ $key } = $svc_domain->$keymap();
140     }
141   } else {
142     my $keymap = $map{$self->option('primary_key')};
143     $primary_key{ $self->option('primary_key') } = $svc_domain->$keymap(),
144   }
145
146   my $err_or_queue = $self->domain_sql_queue(
147     $svc_domain->svcnum,
148     'delete',
149     $self->option('table'),
150     %primary_key,
151     #$self->option('primary_key') => $svc_domain->$keymap(),
152   );
153   return $err_or_queue unless ref($err_or_queue);
154   '';
155 }
156
157 sub domain_sql_queue {
158   my( $self, $svcnum, $method ) = (shift, shift, shift);
159   my $queue = new FS::queue {
160     'svcnum' => $svcnum,
161     'job'    => "FS::part_export::domain_sql::domain_sql_$method",
162   };
163   $queue->insert(
164     $self->option('datasrc'),
165     $self->option('username'),
166     $self->option('password'),
167     @_,
168   ) or $queue;
169 }
170
171 sub domain_sql_insert { #subroutine, not method
172   my $dbh = domain_sql_connect(shift, shift, shift);
173   my( $table, %record ) = @_;
174
175   my $sth = $dbh->prepare(
176     "INSERT INTO $table ( ". join(", ", keys %record).
177     " ) VALUES ( ". join(", ", map '?', keys %record ). " )"
178   ) or die $dbh->errstr;
179
180   $sth->execute( values(%record) )
181     or die "can't insert into $table table: ". $sth->errstr;
182
183   $dbh->disconnect;
184 }
185
186 sub domain_sql_delete { #subroutine, not method
187   my $dbh = domain_sql_connect(shift, shift, shift);
188   my( $table, %record ) = @_;
189
190   my $sth = $dbh->prepare(
191     "DELETE FROM $table WHERE ". join(' AND ', map "$_ = ? ", keys %record )
192   ) or die $dbh->errstr;
193
194   $sth->execute( map $record{$_}, keys %record )
195     or die "can't delete from $table table: ". $sth->errstr;
196
197   $dbh->disconnect;
198 }
199
200 sub domain_sql_replace { #subroutine, not method
201   my $dbh = domain_sql_connect(shift, shift, shift);
202
203   my( $table, $pkey ) = ( shift, shift );
204
205   my %primary_key = ();
206   if ( $pkey =~ /,/ ) {
207     foreach my $key ( split(/\s*,\s*/, $pkey ) ) {
208       $primary_key{$key} = shift;
209     }
210   } else {
211     $primary_key{$pkey} = shift;
212   }
213
214   my %record = @_;
215
216   my $sth = $dbh->prepare(
217     "UPDATE $table".
218     ' SET '.   join(', ',    map "$_ = ?", keys %record      ).
219     ' WHERE '. join(' AND ', map "$_ = ?", keys %primary_key )
220   ) or die $dbh->errstr;
221
222   $sth->execute( values(%record), values(%primary_key) );
223
224   $dbh->disconnect;
225 }
226
227 sub domain_sql_connect {
228   #my($datasrc, $username, $password) = @_;
229   #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
230   DBI->connect(@_) or die $DBI::errstr;
231 }
232
233 1;
234