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