From: timriker Date: Sun, 3 Nov 2002 05:21:01 +0000 (+0000) Subject: add initial SQLite support X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=281d349fc98d4423b03312fc514639fcc610ff80;p=infobot.git add initial SQLite support git-svn-id: https://svn.code.sf.net/p/infobot/code/trunk@595 c11ca15a-4712-0410-83d8-924469b57eb5 --- diff --git a/blootbot/.cvsignore b/blootbot/.cvsignore index 357cc01..e74fd0a 100644 --- a/blootbot/.cvsignore +++ b/blootbot/.cvsignore @@ -1,3 +1,4 @@ blootbot-* blootbot.pid +blootbot.sqlite Temp diff --git a/blootbot/INSTALL b/blootbot/INSTALL index a85aaa1..8ab8336 100644 --- a/blootbot/INSTALL +++ b/blootbot/INSTALL @@ -21,6 +21,7 @@ Method of installation. - Choose your database: - MySQL, read INSTALL.mysql (supported) + - SQLite, read INSTALL.sqlite (unsupported, may work) - PgSQL, read INSTALL.pgsql (unsupported, may work) - Berkeley DBM, read INSTALL.dbm (unsupported, may work) diff --git a/blootbot/INSTALL.dbm b/blootbot/INSTALL.dbm index 93b3db6..02d9c4c 100644 --- a/blootbot/INSTALL.dbm +++ b/blootbot/INSTALL.dbm @@ -7,7 +7,7 @@ INSTALLATION of dbm - If the bot crashes, the dbm file may increase in size dramatically, from 900k-1400k to 16m-24m - dbm is a slow but simple form of db. If you want performance, try mysql - or pgsql (NOT YET) + or sqlite (NOT SUPPORTED) or pgsql (NOT YET) = To convert dbm file to mysql table: - run 'scripts/dbm2mysql.pl old-db' to convert dbm database file diff --git a/blootbot/INSTALL.sqlite b/blootbot/INSTALL.sqlite new file mode 100644 index 0000000..a216df2 --- /dev/null +++ b/blootbot/INSTALL.sqlite @@ -0,0 +1,5 @@ +INSTALL.sqlite +---------------- + +- Install SQLite libraries and DBI Perl modules. + - Debian: (apt-get install libsqlite0 libdbd-sqlite-perl) diff --git a/blootbot/files/sample/sample.config.broken b/blootbot/files/sample/sample.config.broken index d5b43e9..d43fa1c 100644 --- a/blootbot/files/sample/sample.config.broken +++ b/blootbot/files/sample/sample.config.broken @@ -45,6 +45,7 @@ set maxLogSize 10000000 # [str] Ability to remember/tell factoids # none -- disable. # mysql -- MySQL +# sqlite -- SQLite (libdbd-sqlite-perl) # pgsql -- PostGreSQL (NOT SUPPORTED YET) # dbm -- Berkeley DBM ### REQUIRED by factoids,freshmeat,karma,seen,... diff --git a/blootbot/src/IRC/Schedulers.pl b/blootbot/src/IRC/Schedulers.pl index 9c5bbed..c0c9cf2 100644 --- a/blootbot/src/IRC/Schedulers.pl +++ b/blootbot/src/IRC/Schedulers.pl @@ -243,10 +243,10 @@ sub seenFlushOld { my $max_time = &getChanConfDefault("seenMaxDays", 30) *60*60*24; my $delete = 0; - if ($param{'DBType'} =~ /^pgsql|mysql/i) { + if ($param{'DBType'} =~ /^pgsql|mysql|sqlite/i) { my $query; - if ($param{'DBType'} =~ /^mysql$/i) { + if ($param{'DBType'} =~ /^mysql|sqlite$/i) { $query = "SELECT nick,time FROM seen GROUP BY nick HAVING ". "UNIX_TIMESTAMP() - time > $max_time"; } else { # pgsql. @@ -547,7 +547,7 @@ sub seenFlush { $stats{'new'} = 0; $stats{'old'} = 0; - if ($param{'DBType'} =~ /^(mysql|pgsql)$/i) { + if ($param{'DBType'} =~ /^(mysql|pgsql|sqlite)$/i) { foreach $nick (keys %seencache) { my $retval = &dbReplace("seen", "nick", ( "nick" => lc $seencache{$nick}{'nick'}, diff --git a/blootbot/src/Modules/Countdown.pl b/blootbot/src/Modules/Countdown.pl index e805d8e..b294477 100644 --- a/blootbot/src/Modules/Countdown.pl +++ b/blootbot/src/Modules/Countdown.pl @@ -44,7 +44,7 @@ sub Countdown { ### SQL SPECIFIC. my ($to_days,$dayname,$monname); - if ($param{'DBType'} =~ /^mysql$/i) { + if ($param{'DBType'} =~ /^mysql|sqlite$/i) { $to_days = (&dbRawReturn("SELECT TO_DAYS(NOW()) - TO_DAYS('$sqldate')"))[0]; $dayname = (&dbRawReturn("SELECT DAYNAME('$sqldate')"))[0]; $monname = (&dbRawReturn("SELECT MONTHNAME('$sqldate')"))[0]; diff --git a/blootbot/src/Modules/UserDCC.pl b/blootbot/src/Modules/UserDCC.pl index 578cbc7..8bd29db 100644 --- a/blootbot/src/Modules/UserDCC.pl +++ b/blootbot/src/Modules/UserDCC.pl @@ -358,7 +358,7 @@ sub userDCC { return; } - ### TODO: fix up $op to support mysql/pgsql/dbm(perl) + ### TODO: fix up $op to support mysql/sqlite/pgsql/dbm(perl) ### TODO: => add db/sql specific function to fix this. my @list = &searchTable("factoids", "factoid_key", "factoid_value", $op); diff --git a/blootbot/src/Process.pl b/blootbot/src/Process.pl index cbdb6ca..d4473b4 100644 --- a/blootbot/src/Process.pl +++ b/blootbot/src/Process.pl @@ -338,7 +338,7 @@ sub process { } } - if (&IsParam("factoids") and $param{'DBType'} =~ /^(mysql|pgsql|dbm)/i) { + if (&IsParam("factoids") and $param{'DBType'} =~ /^(mysql|sqlite|pgsql|dbm)/i) { &FactoidStuff(); } elsif ($param{'DBType'} =~ /^none$/i) { return "NO FACTOIDS."; diff --git a/blootbot/src/db_sqlite.pl b/blootbot/src/db_sqlite.pl new file mode 100644 index 0000000..6b77aa5 --- /dev/null +++ b/blootbot/src/db_sqlite.pl @@ -0,0 +1,534 @@ +# +# db_sqlite.pl: SQLite database frontend. +# Author: Tim Riker +# Version: 0.1 (20021101) +# Created: 20021101 +# + +package main; +eval "use DBI"; + +if (&IsParam("useStrict")) { use strict; } + +##### +# &openDB($dbname, $sqluser, $sqlpass, $nofail); +sub openDB { + my ($db, $user, $pass, $no_fail) = @_; + my $dsn = "DBI:SQLite:dbname=$db.sqlite"; + $dbh = DBI->connect($dsn,$user,$pass); + + if ($dbh) { + &status("Opened SQLite connection $dsn"); + } else { + &ERROR("cannot connect $dsn."); + &ERROR("since SQLite is not available, shutting down bot!"); + &closePID(); + &closeSHM($shm); + &closeLog(); + + return if ($no_fail); + + exit 1; + } +} + +sub closeDB { + return 0 unless ($dbh); + + my $hoststr = ""; + $hoststr = " to $param{'SQLHost'}" if (exists $param{'SQLHost'}); + + &status("Closed SQLite connection$hoststr."); + $dbh->disconnect(); + + return 1; +} + +##### +# Usage: &dbQuote($str); +sub dbQuote { + return $dbh->quote($_[0]); +} + +##### +# Usage: &dbGet($table, $select, $where); +sub dbGet { + my ($table, $select, $where) = @_; + my $query = "SELECT $select FROM $table"; + $query .= " WHERE $where" if ($where); + + if (!defined $select or $select =~ /^\s*$/) { + &WARN("dbGet: select == NULL."); + return; + } + + if (!defined $table or $table =~ /^\s*$/) { + &WARN("dbGet: table == NULL."); + return; + } + + my $sth; + if (!($sth = $dbh->prepare($query))) { + &ERROR("Get: prepare: $DBI::errstr"); + return; + } + + &SQLDebug($query); + if (!$sth->execute) { + &ERROR("Get: execute: '$query'"); + $sth->finish; + return 0; + } + + my @retval = $sth->fetchrow_array; + + $sth->finish; + + if (scalar @retval > 1) { + return @retval; + } elsif (scalar @retval == 1) { + return $retval[0]; + } else { + return; + } +} + +##### +# Usage: &dbGetCol($table, $select, $where, [$type]); +sub dbGetCol { + my ($table, $select, $where, $type) = @_; + my $query = "SELECT $select FROM $table"; + $query .= " WHERE ".$where if ($where); + my %retval; + + my $sth = $dbh->prepare($query); + &SQLDebug($query); + if (!$sth->execute) { + &ERROR("GetCol: execute: '$query'"); + $sth->finish; + return; + } + + if (defined $type and $type == 2) { + &DEBUG("dbgetcol: type 2!"); + while (my @row = $sth->fetchrow_array) { + $retval{$row[0]} = join(':', $row[1..$#row]); + } + &DEBUG("dbgetcol: count => ".scalar(keys %retval) ); + + } elsif (defined $type and $type == 1) { + while (my @row = $sth->fetchrow_array) { + # reverse it to make it easier to count. + if (scalar @row == 2) { + $retval{$row[1]}{$row[0]} = 1; + } elsif (scalar @row == 3) { + $retval{$row[1]}{$row[0]} = 1; + } + # what to do if there's only one or more than 3? + } + + } else { + while (my @row = $sth->fetchrow_array) { + $retval{$row[0]} = $row[1]; + } + } + + $sth->finish; + + return %retval; +} + +##### +# Usage: &dbGetColNiceHash($table, $select, $where); +sub dbGetColNiceHash { + my ($table, $select, $where) = @_; + $select ||= "*"; + my $query = "SELECT $select FROM $table"; + $query .= " WHERE ".$where if ($where); + my %retval; + + my $sth = $dbh->prepare($query); + &SQLDebug($query); + if (!$sth->execute) { + &ERROR("GetColNiceHash: execute: '$query'"); +# &ERROR("GetCol => $DBI::errstr"); + $sth->finish; + return; + } + + %retval = %{ $sth->fetchrow_hashref() }; + + $sth->finish; + + return %retval; +} + +#### +# Usage: &dbGetColInfo($table); +sub dbGetColInfo { + my ($table) = @_; + + my $query = "SHOW COLUMNS from $table"; + my %retval; + + my $sth = $dbh->prepare($query); + &SQLDebug($query); + if (!$sth->execute) { + &ERROR("GRI => '$query'"); + &ERROR("GRI => $DBI::errstr"); + $sth->finish; + return; + } + + my @cols; + while (my @row = $sth->fetchrow_array) { + push(@cols, $row[0]); + } + $sth->finish; + + return @cols; +} + +##### +# Usage: &dbSet($table, $primhash_ref, $hash_ref); +# Note: dbSet does dbQuote. +sub dbSet { + my ($table, $phref, $href) = @_; + my $where = join(' AND ', map { + $_."=".&dbQuote($phref->{$_}) + } keys %{$phref} + ); + + if (!defined $phref) { + &WARN("dbset: phref == NULL."); + return; + } + + if (!defined $href) { + &WARN("dbset: href == NULL."); + return; + } + + if (!defined $table) { + &WARN("dbset: table == NULL."); + return; + } + + my $result = &dbGet($table, join(',', keys %{$phref}), $where); + + my(@keys,@vals); + foreach (keys %{$href}) { + push(@keys, $_); + push(@vals, &dbQuote($href->{$_}) ); + } + + if (!@keys or !@vals) { + &WARN("dbset: keys or vals is NULL."); + return; + } + + my $query; + if (defined $result) { + my @keyval; + for(my$i=0; $i{$_}) ); + } + + $query = sprintf("INSERT INTO $table (%s) VALUES (%s)", + join(',',@keys), join(',',@vals) ); + } + + &dbRaw("Set", $query); + + return 1; +} + +##### +# Usage: &dbUpdate($table, $primkey, $primval, %hash); +# Note: dbUpdate does dbQuote. +sub dbUpdate { + my ($table, $primkey, $primval, %hash) = @_; + my (@array); + + foreach (keys %hash) { + push(@array, "$_=".&dbQuote($hash{$_}) ); + } + + &dbRaw("Update", "UPDATE $table SET ".join(', ', @array). + " WHERE $primkey=".&dbQuote($primval) + ); + + return 1; +} + +##### +# Usage: &dbInsert($table, $primkey, %hash); +# Note: dbInsert does dbQuote. +sub dbInsert { + my ($table, $primkey, %hash, $delay) = @_; + my (@keys, @vals); + my $p = ""; + + if ($delay) { + &DEBUG("dbI: delay => $delay"); + $p = " DELAYED"; + } + + foreach (keys %hash) { + push(@keys, $_); + push(@vals, &dbQuote($hash{$_})); + } + + &dbRaw("Insert($table)", "INSERT $p INTO $table (".join(',',@keys). + ") VALUES (".join(',',@vals).")" + ); + + return 1; +} + +##### +# Usage: &dbReplace($table, $key, %hash); +# Note: dbReplace does optional dbQuote. +sub dbReplace { + my ($table, $key, %hash) = @_; + my (@keys, @vals); + + foreach (keys %hash) { + if (s/^-//) { # as is. + push(@keys, $_); + push(@vals, $hash{'-'.$_}); + } else { + push(@keys, $_); + push(@vals, &dbQuote($hash{$_})); + } + } + + if (0) { + &DEBUG("REPLACE INTO $table (".join(',',@keys). + ") VALUES (". join(',',@vals). ")" ); + } + + &dbRaw("Replace($table)", "REPLACE INTO $table (".join(',',@keys). + ") VALUES (". join(',',@vals). ")" + ); + + return 1; +} + +##### +# Usage: &dbSetRow($table, $vref, $delay); +# Note: dbSetRow does dbQuote. +sub dbSetRow ($@$) { + my ($table, $vref, $delay) = @_; + my $p = ($delay) ? " DELAYED " : ""; + + # see 'perldoc perlreftut' + my @values; + foreach (@{ $vref }) { + push(@values, &dbQuote($_) ); + } + + if (!scalar @values) { + &WARN("dbSetRow: values array == NULL."); + return; + } + + return &dbRaw("SetRow", "INSERT $p INTO $table VALUES (". + join(",", @values) .")" ); +} + +##### +# Usage: &dbDel($table, $primkey, $primval, [$key]); +# Note: dbDel does dbQuote +sub dbDel { + my ($table, $primkey, $primval, $key) = @_; + + &dbRaw("Del", "DELETE FROM $table WHERE $primkey=". + &dbQuote($primval) + ); + + return 1; +} + +# Usage: &dbRaw($prefix,$rawquery); +sub dbRaw { + my ($prefix,$query) = @_; + my $sth; + + if (!($sth = $dbh->prepare($query))) { + &ERROR("Raw($prefix): $DBI::errstr"); + return 0; + } + +# &DEBUG("query => '$query'."); + + &SQLDebug($query); + if (!$sth->execute) { + &ERROR("Raw($prefix): => '$query'"); + # $DBI::errstr is printed as warning automatically. + $sth->finish; + return 0; + } + + $sth->finish; + + return 1; +} + +# Usage: &dbRawReturn($rawquery); +sub dbRawReturn { + my ($query) = @_; + my @retval; + + my $sth = $dbh->prepare($query); + &SQLDebug($query); + &ERROR("RawReturn => '$query'.") unless $sth->execute; + while (my @row = $sth->fetchrow_array) { + push(@retval, $row[0]); + } + $sth->finish; + + return @retval; +} + +#################################################################### +##### Misc DBI stuff... +##### + +##### +# Usage: &countKeys($table, [$col]); +sub countKeys { + my ($table, $col) = @_; + $col ||= "*"; + &DEBUG("&countKeys($table, $col)"); + + return (&dbRawReturn("SELECT count($col) FROM $table"))[0]; +} + +# Usage: &sumKey($table, $col); +sub sumKey { + my ($table, $col) = @_; + + return (&dbRawReturn("SELECT sum($col) FROM $table"))[0]; +} + +##### +# Usage: &randKey($table, $select); +sub randKey { + my ($table, $select) = @_; + my $rand = int(rand(&countKeys($table) - 1)); + my $query = "SELECT $select FROM $table LIMIT $rand,1"; + + my $sth = $dbh->prepare($query); + &SQLDebug($query); + &WARN("randKey($query)") unless $sth->execute; + my @retval = $sth->fetchrow_array; + $sth->finish; + + return @retval; +} + +##### +# Usage: &deleteTable($table); +sub deleteTable { + &dbRaw("deleteTable($_[0])", "DELETE FROM $_[0]"); +} + +##### +# Usage: &searchTable($table, $select, $key, $str); +# Note: searchTable does dbQuote. +sub searchTable { + my($table, $select, $key, $str) = @_; + my $origStr = $str; + my @results; + + # allow two types of wildcards. + if ($str =~ /^\^(.*)\$$/) { + &DEBUG("searchTable: should use dbGet(), heh."); + $str = $1; + } else { + $str .= "%" if ($str =~ s/^\^//); + $str = "%".$str if ($str =~ s/\$$//); + $str = "%".$str."%" if ($str eq $origStr); # el-cheapo fix. + } + + $str =~ s/\_/\\_/g; + $str =~ s/\?/_/g; # '.' should be supported, too. + $str =~ s/\*/%/g; # for sqlite. + # end of string fix. + + my $query = "SELECT $select FROM $table WHERE $key LIKE ". + &dbQuote($str); + my $sth = $dbh->prepare($query); + &DEBUG("query => '$query'."); + &SQLDebug($query); + if (!$sth->execute) { + &WARN("Search($query)"); + return; + } + + while (my @row = $sth->fetchrow_array) { + push(@results, $row[0]); + } + $sth->finish; + + return @results; +} + +sub dbCreateTable { + my($table) = @_; + my(@path) = ($bot_data_dir, ".","..","../.."); + my $found = 0; + my $data; + + foreach (@path) { + my $file = "$_/setup/$table.sql"; + &DEBUG("dbCT: file => $file"); + next unless ( -f $file ); + + &DEBUG("dbCT: found!!!"); + + open(IN, $file); + while () { + chop; + $data .= $_; + } + + $found++; + last; + } + + if (!$found) { + return 0; + } else { + &dbRaw("createTable($table)", $data); + return 1; + } +} + +sub checkTables { + # retrieve a list of tables's from the server. + my %db; + foreach (&dbRawReturn("SELECT name FROM sqlite_master WHERE type='table'")) + { + $db{$_} = 1; + } + + foreach ("factoids", "freshmeat", "rootwarn", "seen", "stats") { + next if (exists $db{$_}); + &status("checkTables: creating $_..."); + + &dbCreateTable($_); + } +} + +1; diff --git a/blootbot/src/modules.pl b/blootbot/src/modules.pl index 0861982..ae2dac5 100644 --- a/blootbot/src/modules.pl +++ b/blootbot/src/modules.pl @@ -105,12 +105,12 @@ sub loadDBModules { &status("Loading pgsql support."); require "$bot_src_dir/db_pgsql.pl"; &showProc(" (pgsql)"); - } elsif ($param{'DBType'} =~ /^dbm$/i) { - &status("Loading Berkeley DBM support."); - $f = "$bot_src_dir/db_dbm.pl"; - require $f; + } elsif ($param{'DBType'} =~ /^sqlite$|^dbm$/i) { + &status("Loading " . $param{'DBType'} . " support."); + $f="$bot_src_dir/db_" . $param{'DBType'} . ".pl"; $moduleAge{$f} = (stat $f)[9]; - &showProc(" $bot_src_dir/db_dbm.pl"); + require $f; + &showProc(" $bot_src_dir/db_" . $param{'DBType'} . ".pl"); } else { &status("DB support DISABLED."); return;