]> git.donarmstrong.com Git - infobot.git/blob - src/db_mysql.pl
325069d98fb2e7f7b7b1bb76f4062b95aaab547b
[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     # todo: get column names, do $hash{$primkey}{blah} = ...
149     while (my @row = $sth->fetchrow_array) {
150         # reverse it to make it easier to count.
151     }
152
153     $sth->finish;
154
155     return %retval;
156 }
157
158 ####
159 # Usage: &dbGetColInfo($table);
160 sub dbGetColInfo {
161     my ($table) = @_;
162
163     my $query = "SHOW COLUMNS from $table";
164     my %retval;
165
166     my $sth = $dbh->prepare($query);
167     &SQLDebug($query);
168     if (!$sth->execute) {
169         &ERROR("GRI => '$query'");
170         &ERROR("GRI => $DBI::errstr");
171         $sth->finish;
172         return;
173     }
174
175     my @cols;
176     while (my @row = $sth->fetchrow_array) {
177         push(@cols, $row[0]);
178     }
179     $sth->finish;
180
181     return @cols;
182 }
183
184 #####
185 # Usage: &dbSet($table, $primhash_ref, $hash_ref);
186 #  Note: dbSet does dbQuote.
187 sub dbSet {
188     my ($table, $phref, $href) = @_;
189     my $where = join(' AND ', map {
190                 $_."=".&dbQuote($phref->{$_})
191         } keys %{$phref}
192     );
193
194     my $result = &dbGet($table, join(',', keys %{$phref}), $where);
195
196     my(@keys,@vals);
197     foreach (keys %{$href}) {
198         push(@keys, $_);
199         push(@vals, &dbQuote($href->{$_}) );
200     }
201
202     if (!@keys or !@vals) {
203         &WARN("dbset: keys or vals is NULL.");
204         return;
205     }
206
207     my $query;
208     if (defined $result) {
209         my @keyval;
210         for(my$i=0; $i<scalar @keys; $i++) {
211             push(@keyval, $keys[$i]."=".$vals[$i] );
212         }
213
214         $query = "UPDATE $table SET ".
215                 join(' AND ', @keyval).
216                 " WHERE ".$where;
217     } else {
218         foreach (keys %{$phref}) {
219             push(@keys, $_);
220             push(@vals, &dbQuote($phref->{$_}) );
221         }
222
223         $query = sprintf("INSERT INTO $table (%s) VALUES (%s)",
224                 join(',',@keys), join(',',@vals) );
225     }
226
227     &dbRaw("Set", $query);
228
229     return 1;
230 }
231
232 #####
233 # Usage: &dbUpdate($table, $primkey, $primval, %hash);
234 #  Note: dbUpdate does dbQuote.
235 sub dbUpdate {
236     my ($table, $primkey, $primval, %hash) = @_;
237     my (@array);
238
239     foreach (keys %hash) {
240         push(@array, "$_=".&dbQuote($hash{$_}) );
241     }
242
243     &dbRaw("Update", "UPDATE $table SET ".join(', ', @array).
244                 " WHERE $primkey=".&dbQuote($primval)
245     );
246
247     return 1;
248 }
249
250 #####
251 # Usage: &dbInsert($table, $primkey, %hash);
252 #  Note: dbInsert does dbQuote.
253 sub dbInsert {
254     my ($table, $primkey, %hash, $delay) = @_;
255     my (@keys, @vals);
256     my $p       = "";
257
258     if ($delay) {
259         &DEBUG("dbI: delay => $delay");
260         $p      = " DELAYED";
261     }
262
263     foreach (keys %hash) {
264         push(@keys, $_);
265         push(@vals, &dbQuote($hash{$_}));
266     }
267
268     &dbRaw("Insert($table)", "INSERT $p INTO $table (".join(',',@keys).
269                 ") VALUES (".join(',',@vals).")"
270     );
271
272     return 1;
273 }
274
275 #####
276 # Usage: &dbReplace($table, %hash);
277 #  Note: dbReplace does optional dbQuote.
278 sub dbReplace {
279     my ($table, %hash) = @_;
280     my (@keys, @vals);
281
282     foreach (keys %hash) {
283         if (s/^-//) {   # as is.
284             push(@keys, $_);
285             push(@vals, $hash{'-'.$_});
286         } else {
287             push(@keys, $_);
288             push(@vals, &dbQuote($hash{$_}));
289         }
290     }
291
292     if (0) {
293         &DEBUG("REPLACE INTO $table (".join(',',@keys).
294                 ") VALUES (". join(',',@vals). ")" );
295     }
296
297     &dbRaw("Replace($table)", "REPLACE INTO $table (".join(',',@keys).
298                 ") VALUES (". join(',',@vals). ")"
299     );
300
301     return 1;
302 }
303
304 #####
305 # Usage: &dbSetRow($table, $vref, $delay);
306 #  Note: dbSetRow does dbQuote.
307 sub dbSetRow ($@$) {
308     my ($table, $vref, $delay) = @_;
309     my $p       = ($delay) ? " DELAYED " : "";
310
311     # see 'perldoc perlreftut'
312     my @values;
313     foreach (@{ $vref }) {
314         push(@values, &dbQuote($_) );
315     }
316
317     if (!scalar @values) {
318         &WARN("dbSetRow: values array == NULL.");
319         return;
320     }
321
322     return &dbRaw("SetRow", "INSERT $p INTO $table VALUES (".
323         join(",", @values) .")" );
324 }
325
326 #####
327 # Usage: &dbDel($table, $primkey, $primval, [$key]);
328 #  Note: dbDel does dbQuote
329 sub dbDel {
330     my ($table, $primkey, $primval, $key) = @_;
331
332     &dbRaw("Del", "DELETE FROM $table WHERE $primkey=".
333                 &dbQuote($primval)
334     );
335
336     return 1;
337 }
338
339 # Usage: &dbRaw($prefix,$rawquery);
340 sub dbRaw {
341     my ($prefix,$query) = @_;
342     my $sth;
343
344     if (!($sth = $dbh->prepare($query))) {
345         &ERROR("Raw($prefix): $DBI::errstr");
346         return 0;
347     }
348
349 #    &DEBUG("query => '$query'.");
350
351     &SQLDebug($query);
352     if (!$sth->execute) {
353         &ERROR("Raw($prefix): => '$query'");
354         # $DBI::errstr is printed as warning automatically.
355         $sth->finish;
356         return 0;
357     }
358
359     $sth->finish;
360
361     return 1;
362 }
363
364 # Usage: &dbRawReturn($rawquery);
365 sub dbRawReturn {
366     my ($query) = @_;
367     my @retval;
368
369     my $sth = $dbh->prepare($query);
370     &SQLDebug($query);
371     &ERROR("RawReturn => '$query'.") unless $sth->execute;
372     while (my @row = $sth->fetchrow_array) {
373         push(@retval, $row[0]);
374     }
375     $sth->finish;
376
377     return @retval;
378 }
379
380 ####################################################################
381 ##### Misc DBI stuff...
382 #####
383
384 #####
385 # Usage: &countKeys($table, [$col]);
386 sub countKeys {
387     my ($table, $col) = @_;
388     $col ||= "*";
389
390     return (&dbRawReturn("SELECT count($col) FROM $table"))[0];
391 }
392
393 # Usage: &sumKey($table, $col);
394 sub sumKey {
395     my ($table, $col) = @_;
396
397     return (&dbRawReturn("SELECT sum($col) FROM $table"))[0];
398 }
399
400 #####
401 # Usage: &randKey($table, $select);
402 sub randKey {
403     my ($table, $select) = @_;
404     my $rand    = int(rand(&countKeys($table) - 1));
405     my $query   = "SELECT $select FROM $table LIMIT $rand,1";
406
407     my $sth     = $dbh->prepare($query);
408     &SQLDebug($query);
409     &WARN("randKey($query)") unless $sth->execute;
410     my @retval  = $sth->fetchrow_array;
411     $sth->finish;
412
413     return @retval;
414 }
415
416 #####
417 # Usage: &deleteTable($table);
418 sub deleteTable {
419     &dbRaw("deleteTable($_[0])", "DELETE FROM $_[0]");
420 }
421
422 #####
423 # Usage: &searchTable($table, $select, $key, $str);
424 #  Note: searchTable does dbQuote.
425 sub searchTable {
426     my($table, $select, $key, $str) = @_;
427     my $origStr = $str;
428     my @results;
429
430     # allow two types of wildcards.
431     if ($str =~ /^\^(.*)\$$/) {
432         &DEBUG("searchTable: should use dbGet(), heh.");
433         $str = $1;
434     } else {
435         $str .= "%"     if ($str =~ s/^\^//);
436         $str = "%".$str if ($str =~ s/\$$//);
437         $str = "%".$str."%" if ($str eq $origStr);      # el-cheapo fix.
438     }
439
440     $str =~ s/\_/\\_/g;
441     $str =~ s/\?/\_/g;  # '.' should be supported, too.
442     # end of string fix.
443
444     my $query = "SELECT $select FROM $table WHERE $key LIKE ". 
445                 &dbQuote($str);
446     my $sth = $dbh->prepare($query);
447     &SQLDebug($query);
448     if (!$sth->execute) {
449         &WARN("Search($query)");
450         return;
451     }
452
453     while (my @row = $sth->fetchrow_array) {
454         push(@results, $row[0]);
455     }
456     $sth->finish;
457
458     return @results;
459 }
460
461 ####################################################################
462 ##### Factoid related stuff...
463 #####
464
465 #####
466 # Usage: &getFactInfo($faqtoid, $type);
467 #  Note: getFactInfo does dbQuote
468 sub getFactInfo {
469     return &dbGet("factoids", $_[1], "factoid_key=".&dbQuote($_[0]) );
470 }
471
472 #####
473 # Usage: &getFactoid($faqtoid);
474 sub getFactoid {
475     return &getFactInfo($_[0], "factoid_value");
476 }
477
478 #####
479 # Usage: &delFactoid($faqtoid);
480 sub delFactoid {
481     my ($faqtoid) = @_;
482
483     &dbDel("factoids", "factoid_key",$faqtoid);
484     &status("DELETED '$faqtoid'");
485
486     return 1;
487 }
488
489 sub dbCreateTable {
490     my($table)  = @_;
491     my(@path)   = (".","..","../..");
492     my $found   = 0;
493     my $data;
494
495     foreach (@path) {
496         my $file = "$_/setup/$table.sql";
497         &DEBUG("dbCT: file => $file");
498         next unless ( -f $file );
499
500         &DEBUG("dbCT: found!!!");
501
502         open(IN, $file);
503         while (<IN>) {
504             chop;
505             $data .= $_;
506         }
507
508         $found++;
509         last;
510     }
511
512     if (!$found) {
513         return 0;
514     } else {
515         &dbRaw("createTable($table)", $data);
516         return 1;
517     }
518 }
519
520 sub checkTables {
521     my $database_exists = 0;
522     foreach ( &dbRawReturn("SHOW DATABASES") ) {
523         $database_exists++ if ($_ eq $param{'DBName'});
524     }
525
526     unless ($database_exists) {
527         &status("Creating database $param{DBName}...");
528         $query = "CREATE DATABASE $param{DBName}";
529         &dbRaw("create(db $param{DBName})", $query);
530     }
531
532     # retrieve a list of db's from the server.
533     my %db;
534     foreach ($dbh->func('_ListTables')) {
535         $db{$_} = 1;
536     }
537
538     # create database.
539     if (!scalar keys %db) {
540 #       &status("Creating database $param{'DBName'}...");
541 #       $query = "CREATE DATABASE $param{'DBName'}";
542 #       &dbRaw("create(db $param{'DBName'})", $query);
543     }
544
545     foreach ("factoids", "freshmeat", "rootwarn", "seen", "stats",
546     ) {
547         next if (exists $db{$_});
548         &status("  creating new table $_...");
549
550         &dbCreateTable($_);
551     }
552 }
553
554 1;