]> git.donarmstrong.com Git - infobot.git/blob - src/db_mysql.pl
- berkeley dbm support now works! thanks to tim riker.
[infobot.git] / src / db_mysql.pl
1 #
2 # db_mysql.pl: MySQL database frontend.
3 #      Author: dms
4 #     Version: v0.2c (19991224)
5 #     Created: 19991203
6 #
7
8 package main;
9
10 if (&IsParam("useStrict")) { use strict; }
11
12 #####
13 # &openDB($dbname, $sqluser, $sqlpass, $nofail);
14 sub openDB {
15     my ($db, $user, $pass, $no_fail) = @_;
16     my $dsn = "DBI:mysql:$db";
17     my $hoststr = "";
18     if (exists $param{'SQLHost'} and $param{'SQLHost'}) {
19         $dsn    .= ":$param{SQLHost}";
20         $hoststr = " to $param{'SQLHost'}";
21     }
22     $dbh    = DBI->connect($dsn, $user, $pass);
23
24     if ($dbh) {
25         &status("Opened MySQL connection$hoststr");
26     } else {
27         &ERROR("cannot connect$hoststr.");
28         &ERROR("since mysql is not available, shutting down bot!");
29         &closePID();
30         &closeSHM($shm);
31         &closeLog();
32
33         return if ($no_fail);
34
35         exit 1;
36     }
37 }
38
39 sub closeDB {
40     return 0 unless ($dbh);
41
42     my $hoststr = "";
43     $hoststr = " to $param{'SQLHost'}" if (exists $param{'SQLHost'});
44
45     &status("Closed MySQL connection$hoststr.");
46     $dbh->disconnect();
47
48     return 1;
49 }
50
51 #####
52 # Usage: &dbQuote($str);
53 sub dbQuote {
54     return $dbh->quote($_[0]);
55 }
56
57 #####
58 # Usage: &dbGet($table, $select, $where);
59 sub dbGet {
60     my ($table, $select, $where) = @_;
61     my $query   = "SELECT $select FROM $table";
62     $query      .= " WHERE $where" if ($where);
63
64     if (!defined $select or $select =~ /^\s*$/) {
65         &WARN("dbGet: select == NULL.");
66         return;
67     }
68
69     if (!defined $table or $table =~ /^\s*$/) {
70         &WARN("dbGet: table == NULL.");
71         return;
72     }
73
74     my $sth;
75     if (!($sth = $dbh->prepare($query))) {
76         &ERROR("Get: prepare: $DBI::errstr");
77         return;
78     }
79
80     &SQLDebug($query);
81     if (!$sth->execute) {
82         &ERROR("Get: execute: '$query'");
83         $sth->finish;
84         return 0;
85     }
86
87     my @retval = $sth->fetchrow_array;
88
89     $sth->finish;
90
91     if (scalar @retval > 1) {
92         return @retval;
93     } elsif (scalar @retval == 1) {
94         return $retval[0];
95     } else {
96         return;
97     }
98 }
99
100 #####
101 # Usage: &dbGetCol($table, $select, $where, [$type]);
102 sub dbGetCol {
103     my ($table, $select, $where, $type) = @_;
104     my $query   = "SELECT $select FROM $table";
105     $query      .= " WHERE ".$where if ($where);
106     my %retval;
107
108     my $sth = $dbh->prepare($query);
109     &SQLDebug($query);
110     if (!$sth->execute) {
111         &ERROR("GetCol: execute: '$query'");
112         $sth->finish;
113         return;
114     }
115
116     if (defined $type and $type == 2) {
117         &DEBUG("dbgetcol: type 2!");
118         while (my @row = $sth->fetchrow_array) {
119             $retval{$row[0]} = join(':', $row[1..$#row]);
120         }
121         &DEBUG("dbgetcol: count => ".scalar(keys %retval) );
122
123     } elsif (defined $type and $type == 1) {
124         while (my @row = $sth->fetchrow_array) {
125             # reverse it to make it easier to count.
126             if (scalar @row == 2) {
127                 $retval{$row[1]}{$row[0]} = 1;
128             } elsif (scalar @row == 3) {
129                 $retval{$row[1]}{$row[0]} = 1;
130             }
131             # what to do if there's only one or more than 3?
132         }
133
134     } else {
135         while (my @row = $sth->fetchrow_array) {
136             $retval{$row[0]} = $row[1];
137         }
138     }
139
140     $sth->finish;
141
142     return %retval;
143 }
144
145 #####
146 # Usage: &dbGetColNiceHash($table, $select, $where);
147 sub dbGetColNiceHash {
148     my ($table, $select, $where) = @_;
149     $select     ||= "*";
150     my $query   = "SELECT $select FROM $table";
151     $query      .= " WHERE ".$where if ($where);
152     my %retval;
153
154     my $sth = $dbh->prepare($query);
155     &SQLDebug($query);
156     if (!$sth->execute) {
157         &ERROR("GetColNiceHash: execute: '$query'");
158 #       &ERROR("GetCol => $DBI::errstr");
159         $sth->finish;
160         return;
161     }
162
163     %retval = %{ $sth->fetchrow_hashref() };
164
165     $sth->finish;
166
167     return %retval;
168 }
169
170 ####
171 # Usage: &dbGetColInfo($table);
172 sub dbGetColInfo {
173     my ($table) = @_;
174
175     my $query = "SHOW COLUMNS from $table";
176     my %retval;
177
178     my $sth = $dbh->prepare($query);
179     &SQLDebug($query);
180     if (!$sth->execute) {
181         &ERROR("GRI => '$query'");
182         &ERROR("GRI => $DBI::errstr");
183         $sth->finish;
184         return;
185     }
186
187     my @cols;
188     while (my @row = $sth->fetchrow_array) {
189         push(@cols, $row[0]);
190     }
191     $sth->finish;
192
193     return @cols;
194 }
195
196 #####
197 # Usage: &dbSet($table, $primhash_ref, $hash_ref);
198 #  Note: dbSet does dbQuote.
199 sub dbSet {
200     my ($table, $phref, $href) = @_;
201     my $where = join(' AND ', map {
202                 $_."=".&dbQuote($phref->{$_})
203         } keys %{$phref}
204     );
205
206     if (!defined $phref) {
207         &WARN("dbset: phref == NULL.");
208         return;
209     }
210
211     if (!defined $href) {
212         &WARN("dbset: href == NULL.");
213         return;
214     }
215
216     if (!defined $table) {
217         &WARN("dbset: table == NULL.");
218         return;
219     }
220
221     my $result = &dbGet($table, join(',', keys %{$phref}), $where);
222
223     my(@keys,@vals);
224     foreach (keys %{$href}) {
225         push(@keys, $_);
226         push(@vals, &dbQuote($href->{$_}) );
227     }
228
229     if (!@keys or !@vals) {
230         &WARN("dbset: keys or vals is NULL.");
231         return;
232     }
233
234     my $query;
235     if (defined $result) {
236         my @keyval;
237         for(my$i=0; $i<scalar @keys; $i++) {
238             push(@keyval, $keys[$i]."=".$vals[$i] );
239         }
240
241         $query = "UPDATE $table SET ".
242                 join(' AND ', @keyval).
243                 " WHERE ".$where;
244     } else {
245         foreach (keys %{$phref}) {
246             push(@keys, $_);
247             push(@vals, &dbQuote($phref->{$_}) );
248         }
249
250         $query = sprintf("INSERT INTO $table (%s) VALUES (%s)",
251                 join(',',@keys), join(',',@vals) );
252     }
253
254     &dbRaw("Set", $query);
255
256     return 1;
257 }
258
259 #####
260 # Usage: &dbUpdate($table, $primkey, $primval, %hash);
261 #  Note: dbUpdate does dbQuote.
262 sub dbUpdate {
263     my ($table, $primkey, $primval, %hash) = @_;
264     my (@array);
265
266     foreach (keys %hash) {
267         push(@array, "$_=".&dbQuote($hash{$_}) );
268     }
269
270     &dbRaw("Update", "UPDATE $table SET ".join(', ', @array).
271                 " WHERE $primkey=".&dbQuote($primval)
272     );
273
274     return 1;
275 }
276
277 #####
278 # Usage: &dbInsert($table, $primkey, %hash);
279 #  Note: dbInsert does dbQuote.
280 sub dbInsert {
281     my ($table, $primkey, %hash, $delay) = @_;
282     my (@keys, @vals);
283     my $p       = "";
284
285     if ($delay) {
286         &DEBUG("dbI: delay => $delay");
287         $p      = " DELAYED";
288     }
289
290     foreach (keys %hash) {
291         push(@keys, $_);
292         push(@vals, &dbQuote($hash{$_}));
293     }
294
295     &dbRaw("Insert($table)", "INSERT $p INTO $table (".join(',',@keys).
296                 ") VALUES (".join(',',@vals).")"
297     );
298
299     return 1;
300 }
301
302 #####
303 # Usage: &dbReplace($table, $key, %hash);
304 #  Note: dbReplace does optional dbQuote.
305 sub dbReplace {
306     my ($table, $key, %hash) = @_;
307     my (@keys, @vals);
308
309     foreach (keys %hash) {
310         if (s/^-//) {   # as is.
311             push(@keys, $_);
312             push(@vals, $hash{'-'.$_});
313         } else {
314             push(@keys, $_);
315             push(@vals, &dbQuote($hash{$_}));
316         }
317     }
318
319     if (0) {
320         &DEBUG("REPLACE INTO $table (".join(',',@keys).
321                 ") VALUES (". join(',',@vals). ")" );
322     }
323
324     &dbRaw("Replace($table)", "REPLACE INTO $table (".join(',',@keys).
325                 ") VALUES (". join(',',@vals). ")"
326     );
327
328     return 1;
329 }
330
331 #####
332 # Usage: &dbSetRow($table, $vref, $delay);
333 #  Note: dbSetRow does dbQuote.
334 sub dbSetRow ($@$) {
335     my ($table, $vref, $delay) = @_;
336     my $p       = ($delay) ? " DELAYED " : "";
337
338     # see 'perldoc perlreftut'
339     my @values;
340     foreach (@{ $vref }) {
341         push(@values, &dbQuote($_) );
342     }
343
344     if (!scalar @values) {
345         &WARN("dbSetRow: values array == NULL.");
346         return;
347     }
348
349     return &dbRaw("SetRow", "INSERT $p INTO $table VALUES (".
350         join(",", @values) .")" );
351 }
352
353 #####
354 # Usage: &dbDel($table, $primkey, $primval, [$key]);
355 #  Note: dbDel does dbQuote
356 sub dbDel {
357     my ($table, $primkey, $primval, $key) = @_;
358
359     &dbRaw("Del", "DELETE FROM $table WHERE $primkey=".
360                 &dbQuote($primval)
361     );
362
363     return 1;
364 }
365
366 # Usage: &dbRaw($prefix,$rawquery);
367 sub dbRaw {
368     my ($prefix,$query) = @_;
369     my $sth;
370
371     if (!($sth = $dbh->prepare($query))) {
372         &ERROR("Raw($prefix): $DBI::errstr");
373         return 0;
374     }
375
376 #    &DEBUG("query => '$query'.");
377
378     &SQLDebug($query);
379     if (!$sth->execute) {
380         &ERROR("Raw($prefix): => '$query'");
381         # $DBI::errstr is printed as warning automatically.
382         $sth->finish;
383         return 0;
384     }
385
386     $sth->finish;
387
388     return 1;
389 }
390
391 # Usage: &dbRawReturn($rawquery);
392 sub dbRawReturn {
393     my ($query) = @_;
394     my @retval;
395
396     my $sth = $dbh->prepare($query);
397     &SQLDebug($query);
398     &ERROR("RawReturn => '$query'.") unless $sth->execute;
399     while (my @row = $sth->fetchrow_array) {
400         push(@retval, $row[0]);
401     }
402     $sth->finish;
403
404     return @retval;
405 }
406
407 ####################################################################
408 ##### Misc DBI stuff...
409 #####
410
411 #####
412 # Usage: &countKeys($table, [$col]);
413 sub countKeys {
414     my ($table, $col) = @_;
415     $col ||= "*";
416
417     return (&dbRawReturn("SELECT count($col) FROM $table"))[0];
418 }
419
420 # Usage: &sumKey($table, $col);
421 sub sumKey {
422     my ($table, $col) = @_;
423
424     return (&dbRawReturn("SELECT sum($col) FROM $table"))[0];
425 }
426
427 #####
428 # Usage: &randKey($table, $select);
429 sub randKey {
430     my ($table, $select) = @_;
431     my $rand    = int(rand(&countKeys($table) - 1));
432     my $query   = "SELECT $select FROM $table LIMIT $rand,1";
433
434     my $sth     = $dbh->prepare($query);
435     &SQLDebug($query);
436     &WARN("randKey($query)") unless $sth->execute;
437     my @retval  = $sth->fetchrow_array;
438     $sth->finish;
439
440     return @retval;
441 }
442
443 #####
444 # Usage: &deleteTable($table);
445 sub deleteTable {
446     &dbRaw("deleteTable($_[0])", "DELETE FROM $_[0]");
447 }
448
449 #####
450 # Usage: &searchTable($table, $select, $key, $str);
451 #  Note: searchTable does dbQuote.
452 sub searchTable {
453     my($table, $select, $key, $str) = @_;
454     my $origStr = $str;
455     my @results;
456
457     # allow two types of wildcards.
458     if ($str =~ /^\^(.*)\$$/) {
459         &DEBUG("searchTable: should use dbGet(), heh.");
460         $str = $1;
461     } else {
462         $str .= "%"     if ($str =~ s/^\^//);
463         $str = "%".$str if ($str =~ s/\$$//);
464         $str = "%".$str."%" if ($str eq $origStr);      # el-cheapo fix.
465     }
466
467     $str =~ s/\_/\\_/g;
468     $str =~ s/\?/_/g;   # '.' should be supported, too.
469     $str =~ s/\*/%/g;   # for mysql.
470     # end of string fix.
471
472     my $query = "SELECT $select FROM $table WHERE $key LIKE ". 
473                 &dbQuote($str);
474     my $sth = $dbh->prepare($query);
475     &DEBUG("query => '$query'.");
476     &SQLDebug($query);
477     if (!$sth->execute) {
478         &WARN("Search($query)");
479         return;
480     }
481
482     while (my @row = $sth->fetchrow_array) {
483         push(@results, $row[0]);
484     }
485     $sth->finish;
486
487     return @results;
488 }
489
490 sub dbCreateTable {
491     my($table)  = @_;
492     my(@path)   = ($bot_data_dir, ".","..","../..");
493     my $found   = 0;
494     my $data;
495
496     foreach (@path) {
497         my $file = "$_/setup/$table.sql";
498         &DEBUG("dbCT: file => $file");
499         next unless ( -f $file );
500
501         &DEBUG("dbCT: found!!!");
502
503         open(IN, $file);
504         while (<IN>) {
505             chop;
506             $data .= $_;
507         }
508
509         $found++;
510         last;
511     }
512
513     if (!$found) {
514         return 0;
515     } else {
516         &dbRaw("createTable($table)", $data);
517         return 1;
518     }
519 }
520
521 sub checkTables {
522     my $database_exists = 0;
523     foreach ( &dbRawReturn("SHOW DATABASES") ) {
524         $database_exists++ if ($_ eq $param{'DBName'});
525     }
526
527     unless ($database_exists) {
528         &status("Creating database $param{DBName}...");
529         $query = "CREATE DATABASE $param{DBName}";
530         &dbRaw("create(db $param{DBName})", $query);
531     }
532
533     # retrieve a list of db's from the server.
534     my %db;
535     foreach ($dbh->func('_ListTables')) {
536         $db{$_} = 1;
537     }
538
539     # create database.
540     if (!scalar keys %db) {
541 #       &status("Creating database $param{'DBName'}...");
542 #       $query = "CREATE DATABASE $param{'DBName'}";
543 #       &dbRaw("create(db $param{'DBName'})", $query);
544     }
545
546     foreach ("factoids", "freshmeat", "rootwarn", "seen", "stats",
547     ) {
548         next if (exists $db{$_});
549         &status("  creating new table $_...");
550
551         &dbCreateTable($_);
552     }
553 }
554
555 1;