Mysql 5.0 ARCHIVE storage engine für article_plain und article_attachment

Hallo, unser OTRS wächst und wächst und wächst - kein Ende abzusehen. Inzwischen haben wir 1 GB an Daten und es werden immer mehr. Da noch keine Archivierungslösung in Sicht ist und ich mir gerade MySQL 5.0 angeschaut habe kam mir die Idee die neue Stroage Engine von MySQL 5.0 ARCHIVE zu nutzen (des weiteren einfach ARCHIVE genannt) um die Datenbank etwas zu verkleinern. - Die Einschwänkungen der Storage Engine sind : 1) ARCHIVE kann nur INSERT und SELECT 2) ARCHIVE unterstützt keine Indexe 3) ARCHIVE unterstützt bis MySQL 5.1 kein auto_increment Als Kandidaten für die Einsparung habe ich mir article_plain und article_attachment ausgesucht, da diese beide jeweils > 400 MB sind und damit die größten Tabellen bilden. Weiterhin vermute ich, dass in diesen Tabellen von OTRS keine Änderungen vorgenommen (???) und damit kommen wir mit SELECT und INSERT, welche von ARCHIVE unterstützt werden aus. Ist dies richtig ? Eine weitere Frage ist ebenfalls ob die fehlenden Indexe nicht extreme Performance Einbußen bringen ? Ich habe etwas getestet und herausgefunden dass Anfragen teilweise das 1000fache länger dauern. mysql> select article_id, change_time, create_time from article_plain where change_time = '2006-07-27 23:26:06'; +------------+---------------------+---------------------+ | article_id | change_time | create_time | +------------+---------------------+---------------------+ | 9839 | 2006-07-27 23:26:06 | 2006-07-27 23:26:06 | +------------+---------------------+---------------------+ 1 row in set (18.59 sec) mysql> select article_id, change_time, create_time from article_plain where article_id=9839; +------------+---------------------+---------------------+ | article_id | change_time | create_time | +------------+---------------------+---------------------+ | 9839 | 2006-07-27 23:26:06 | 2006-07-27 23:26:06 | +------------+---------------------+---------------------+ 1 row in set (0.01 sec) In diesem Beispiel ist "article_id" mit einem Index versehen, "create_time" jedoch nicht. Ist dieser (zugegeben einfache) Plausibilitätstest korrekt und wird sich dann die Performance im OTRS (OTRS sucht doch immer über article_id oder ?) extrem verschlechtern ? Ich frage deshalb, weil wenn ich eine Volltextsuche im Feld "Text" über das Frontend durchführe dauert diese nur wenige Sekunden. Dieses müsste ja aber auch alle article_plain durchsuchen. Daher die Vermutung, dassmein einfacher Test vielleicht nicht repräsentativ ist. Bevor ich das Ganze ausprobiere, wollte ich die Liste erst ein Mal fragen, ob hier Erfahrungen hierzu vorhanden sind. Danke, Robert Heinzmann -- Echte DSL-Flatrate dauerhaft für 0,- Euro*. Nur noch kurze Zeit! "Feel free" mit GMX DSL: http://www.gmx.net/de/go/dsl

Hallo. Viel einfacher wäre es, du speicherst das Zeugs im Dateisystem. Gab auch mal nen Generic Agent in der Mailingliste, der das Ganze verschiebt... -- Mit freundlichen Grüssen André Bauer System: Debian 3.1 / Apache 2.0.54 / MySQL 4.1.11 / OTRS 2.0.4 ============================================ RH> Hallo, RH> unser OTRS wächst und wächst und wächst - kein Ende RH> abzusehen. Inzwischen haben wir 1 GB an Daten und es werden immer RH> mehr. RH> Da noch keine Archivierungslösung in Sicht ist und ich mir RH> gerade MySQL 5.0 angeschaut habe kam mir die Idee die neue Stroage RH> Engine von MySQL 5.0 ARCHIVE zu nutzen (des weiteren einfach RH> ARCHIVE genannt) um die Datenbank etwas zu verkleinern. RH> - Die Einschwänkungen der Storage Engine sind : RH> 1) ARCHIVE kann nur INSERT und SELECT RH> 2) ARCHIVE unterstützt keine Indexe RH> 3) ARCHIVE unterstützt bis MySQL 5.1 kein auto_increment RH> Als Kandidaten für die Einsparung habe ich mir article_plain RH> und article_attachment ausgesucht, da diese beide jeweils > 400 MB RH> sind und damit die größten Tabellen bilden. Weiterhin vermute ich, RH> dass in diesen Tabellen von OTRS keine Änderungen vorgenommen RH> (???) und damit kommen wir mit SELECT und INSERT, welche von RH> ARCHIVE unterstützt werden aus. RH> Ist dies richtig ? RH> Eine weitere Frage ist ebenfalls ob die fehlenden Indexe RH> nicht extreme Performance Einbußen bringen ? Ich habe etwas RH> getestet und herausgefunden dass Anfragen teilweise das 1000fache RH> länger dauern. mysql>> select article_id, change_time, create_time from mysql>> article_plain where change_time = '2006-07-27 23:26:06'; RH> +------------+---------------------+---------------------+ RH> | article_id | change_time | create_time | RH> +------------+---------------------+---------------------+ RH> | 9839 | 2006-07-27 23:26:06 | 2006-07-27 23:26:06 | RH> +------------+---------------------+---------------------+ RH> 1 row in set (18.59 sec) mysql>> select article_id, change_time, create_time from mysql>> article_plain where article_id=9839; RH> +------------+---------------------+---------------------+ RH> | article_id | change_time | create_time | RH> +------------+---------------------+---------------------+ RH> | 9839 | 2006-07-27 23:26:06 | 2006-07-27 23:26:06 | RH> +------------+---------------------+---------------------+ RH> 1 row in set (0.01 sec) RH> In diesem Beispiel ist "article_id" mit einem Index versehen, "create_time" jedoch nicht. RH> Ist dieser (zugegeben einfache) Plausibilitätstest korrekt RH> und wird sich dann die Performance im OTRS (OTRS sucht doch immer RH> über article_id oder ?) extrem verschlechtern ? RH> Ich frage deshalb, weil wenn ich eine Volltextsuche im Feld RH> "Text" über das Frontend durchführe dauert diese nur wenige RH> Sekunden. Dieses müsste ja aber auch alle article_plain RH> durchsuchen. Daher die Vermutung, dassmein einfacher Test RH> vielleicht nicht repräsentativ ist. RH> Bevor ich das Ganze ausprobiere, wollte ich die Liste erst RH> ein Mal fragen, ob hier Erfahrungen hierzu vorhanden sind. RH> Danke, RH> Robert Heinzmann

Stimmt,
da gab es mal einen Agenten. Ist auch ein Ansatz dem ich noch ein Mal nachgehen werde.
Bezüglich der Datenbanklösung habe ich noch ein mal geforscht. Danach befinden sich in article_plain die e-Mails zusammen mit den attachments - also die kompletten e-Mail Daten (wie der name schon sagt - dumm von mir :).
Diese benötige ich denke ich nur wenn ich im Frontend auf "klar" oder im Englischen Frontent "plain" klicke. Dies tun wir sehr selten bis nie und somit wäre eine Wartezeit von einigen Sekunden (ca 30 derzeit ohne index) auch machbar.
Article_plain ist damit ein kandidat für die Storage Engine ARCHIV.
Testen muss ich das Ganze allerdings noch.
Die Tabelle article_attachment lässt sich sicherlich auch nicht so gut komprimieren, da die meisten Attachments bei schon gepackt sind. Aber auch diese Tabelle währe ein Kandidat, da auch hier der Zugriff nicht super schnell erfolgen muss. Also kann auch hier auf indexe verzichtet werden.
Robert Heinzmann
-------- Original-Nachricht --------
Datum: Fri, 28 Jul 2006 16:05:47 +0200
Von: "André Bauer"
Hallo.
Viel einfacher wäre es, du speicherst das Zeugs im Dateisystem. Gab auch mal nen Generic Agent in der Mailingliste, der das Ganze verschiebt...
-- Mit freundlichen Grüssen André Bauer System: Debian 3.1 / Apache 2.0.54 / MySQL 4.1.11 / OTRS 2.0.4
============================================
-- Echte DSL-Flatrate dauerhaft für 0,- Euro*. Nur noch kurze Zeit! "Feel free" mit GMX DSL: http://www.gmx.net/de/go/dsl

Alles gut ! :)
Ich habe den Agenten gefunden (http://lists.otrs.org/pipermail/otrs-de/2006-February/005646.html) und auch auf unserem Testsystem getestet.
Er funktioniert mit aktuellen OTRS versionne leider nicht, da die Funktionen GetTicket, GetArticle in ArticleGet und TicketGet umbenannt wurden.
Anbei eine modifizierte Version, die nun unter 2.0.X funktioniert. Wiederum ohne Garantie auf Funktion!!
Nach dem Bewegen der Tickets und Umstellen des Backend auf StorageFS funktioniert alles gut. Der Vorteil der Auslagerung im Filesystem ist neben dem Geschwindigkeitsvorteil (mySQL DB is nun 1/20el der ursprünglichen Größe) auch, dass selektiv alle Attachments und Plain Mails die älter als ein gewisses Alter sind archiviert werden können und nicht jedes Mal gesichert werden müssen - ein einfaches "find ... -print0 | xargs -0 rm" tut hier Wunder.
Im OTRS äußert sich das Ganze dann so wenn die plain Mails und Attachments to einem Ticket im FS gelöscht wurden:
1) Klickt mann auf "plain" oder "klar", kommt eine Fehlermeldung
2) Attachemnts die nicht im Filesystem vorhanden sind werden im Frontend nicht mehr angezeigt (das Datei Symbol in der Meldung ist weg)
An die OTRS Entwickler: Währe es nicht gut, wenn der "plain" / "klar" Link bur angezeigt wird wenn die Datei auch im Filesystem vorhanden sind (analog den Attachments) ?
Danke noch ein Mal an Stefan Bedorf für den super hilfreichen MoveArticleParts.pm generic agent. Ist sicherlich ein Agent, der auch in die offizielle OTRS Version einfließen sollte.
Grüße,
Robert Heinzmann
-------- Original-Nachricht --------
Datum: Fri, 28 Jul 2006 16:35:49 +0200
Von: "Robert Heinzmann"
Stimmt,
da gab es mal einen Agenten. Ist auch ein Ansatz dem ich noch ein Mal nachgehen werde.
Bezüglich der Datenbanklösung habe ich noch ein mal geforscht. Danach befinden sich in article_plain die e-Mails zusammen mit den attachments - also die kompletten e-Mail Daten (wie der name schon sagt - dumm von mir :).
Diese benötige ich denke ich nur wenn ich im Frontend auf "klar" oder im Englischen Frontent "plain" klicke. Dies tun wir sehr selten bis nie und somit wäre eine Wartezeit von einigen Sekunden (ca 30 derzeit ohne index) auch machbar.
Article_plain ist damit ein kandidat für die Storage Engine ARCHIV.
Testen muss ich das Ganze allerdings noch.
Die Tabelle article_attachment lässt sich sicherlich auch nicht so gut komprimieren, da die meisten Attachments bei schon gepackt sind. Aber auch diese Tabelle währe ein Kandidat, da auch hier der Zugriff nicht super schnell erfolgen muss. Also kann auch hier auf indexe verzichtet werden.
Robert Heinzmann
-- Echte DSL-Flatrate dauerhaft für 0,- Euro*. Nur noch kurze Zeit! "Feel free" mit GMX DSL: http://www.gmx.net/de/go/dsl

Ich habe mit dem Agenten noch ein kleines Problem festgestellt. Er migrierte bisher die Tickets aus der DB ins FS ohne jedoch die Mtime oder CTime entsprechend der Ticket arival Zeit zu setzen. Das kann er nun. Im Anhang der neue Agent. Damit ist es möglich sofort nach der Migration "find . -mtime +XXX -print0 | xargs ..." jobs aufzusetzen. Robert Robert Heinzmann schrieb:
Alles gut ! :)
Ich habe den Agenten gefunden (http://lists.otrs.org/pipermail/otrs-de/2006-February/005646.html) und auch auf unserem Testsystem getestet.
Er funktioniert mit aktuellen OTRS versionne leider nicht, da die Funktionen GetTicket, GetArticle in ArticleGet und TicketGet umbenannt wurden.
Anbei eine modifizierte Version, die nun unter 2.0.X funktioniert. Wiederum ohne Garantie auf Funktion!!
Nach dem Bewegen der Tickets und Umstellen des Backend auf StorageFS funktioniert alles gut. Der Vorteil der Auslagerung im Filesystem ist neben dem Geschwindigkeitsvorteil (mySQL DB is nun 1/20el der ursprünglichen Größe) auch, dass selektiv alle Attachments und Plain Mails die älter als ein gewisses Alter sind archiviert werden können und nicht jedes Mal gesichert werden müssen - ein einfaches "find ... -print0 | xargs -0 rm" tut hier Wunder.
Im OTRS äußert sich das Ganze dann so wenn die plain Mails und Attachments to einem Ticket im FS gelöscht wurden:
1) Klickt mann auf "plain" oder "klar", kommt eine Fehlermeldung 2) Attachemnts die nicht im Filesystem vorhanden sind werden im Frontend nicht mehr angezeigt (das Datei Symbol in der Meldung ist weg)
An die OTRS Entwickler: Währe es nicht gut, wenn der "plain" / "klar" Link bur angezeigt wird wenn die Datei auch im Filesystem vorhanden sind (analog den Attachments) ?
Danke noch ein Mal an Stefan Bedorf für den super hilfreichen MoveArticleParts.pm generic agent. Ist sicherlich ein Agent, der auch in die offizielle OTRS Version einfließen sollte.
Grüße, Robert Heinzmann
# --
# Kernel/System/GenericAgent/MoveArticleParts.pm - move article_plain and article_attachment from db to fs
# Copyright (C) 2005 Stefan Bedorf

Hallo zusammen, also das script ist spitze! Ich hab das Problem das bei uns OTRS seit 2004 läuft und die SQL-DB mittlerweile 5,7 Gig groß ist. Das Script hab ich soweit zum laufen gebracht und die DB ist auf 3,6 Gig geschrumpft. Ich habe nur ein Problem, es werden nur knapp 190 Tage abgearbeitet (Oktober 2006) ich habe auch versucht das script manuell mit dem Parameter zu starten aber es arbeitet immer den selben Zeitraum ab. so hab ich das script z.B.aufgerufen: ./GenericAgent.pl -l 5000 ich hab auch noch andere varianten ausprobiert aber ohne Erfolg. Kann mir jemand helfen? Gruß Robert Heinzmann schrieb:
Ich habe mit dem Agenten noch ein kleines Problem festgestellt.
Er migrierte bisher die Tickets aus der DB ins FS ohne jedoch die Mtime oder CTime entsprechend der Ticket arival Zeit zu setzen. Das kann er nun. Im Anhang der neue Agent. Damit ist es möglich sofort nach der Migration "find . -mtime +XXX -print0 | xargs ..." jobs aufzusetzen.
Robert
Robert Heinzmann schrieb:
Alles gut ! :)
Ich habe den Agenten gefunden (http://lists.otrs.org/pipermail/otrs-de/2006-February/005646.html) und auch auf unserem Testsystem getestet. Er funktioniert mit aktuellen OTRS versionne leider nicht, da die Funktionen GetTicket, GetArticle in ArticleGet und TicketGet umbenannt wurden. Anbei eine modifizierte Version, die nun unter 2.0.X funktioniert. Wiederum ohne Garantie auf Funktion!!
Nach dem Bewegen der Tickets und Umstellen des Backend auf StorageFS funktioniert alles gut. Der Vorteil der Auslagerung im Filesystem ist neben dem Geschwindigkeitsvorteil (mySQL DB is nun 1/20el der ursprünglichen Größe) auch, dass selektiv alle Attachments und Plain Mails die älter als ein gewisses Alter sind archiviert werden können und nicht jedes Mal gesichert werden müssen - ein einfaches "find ... -print0 | xargs -0 rm" tut hier Wunder. Im OTRS äußert sich das Ganze dann so wenn die plain Mails und Attachments to einem Ticket im FS gelöscht wurden: 1) Klickt mann auf "plain" oder "klar", kommt eine Fehlermeldung 2) Attachemnts die nicht im Filesystem vorhanden sind werden im Frontend nicht mehr angezeigt (das Datei Symbol in der Meldung ist weg)
An die OTRS Entwickler: Währe es nicht gut, wenn der "plain" / "klar" Link bur angezeigt wird wenn die Datei auch im Filesystem vorhanden sind (analog den Attachments) ? Danke noch ein Mal an Stefan Bedorf für den super hilfreichen MoveArticleParts.pm generic agent. Ist sicherlich ein Agent, der auch in die offizielle OTRS Version einfließen sollte.
Grüße, Robert Heinzmann
------------------------------------------------------------------------
# -- # Kernel/System/GenericAgent/MoveArticleParts.pm - move article_plain and article_attachment from db to fs # Copyright (C) 2005 Stefan Bedorf
# -- # $Id: MoveArticleParts.pm,v 1.00 2005/09/05 09:34:09 sbedorf Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you # did not receive this file, see http://www.gnu.org/licenses/gpl.txt. # -- package Kernel::System::GenericAgent::MoveArticleParts;
use strict; use File::Path; use File::Basename; use MIME::Words qw(:all);
# -- # to get it writable for the otrs group (just in case) # -- umask 002;
use vars qw($VERSION); $VERSION = '$Revision: 1.11 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- sub new { my $Type = shift; my %Param = @_;
# allocate new hash for object my $Self = {}; bless ($Self, $Type);
# check needed objects foreach (qw(DBObject ConfigObject LogObject TicketObject)) { $Self->{$_} = $Param{$_} || die "Got no $_!"; }
$Self->{UserObject} = Kernel::System::User->new(%Param); $Self->{EmailObject} = Kernel::System::Email->new(%Param); $Self->{QueueObject} = Kernel::System::Queue->new(%Param);
return $Self; } # -- sub Run { my $Self = shift; my %Param = @_;
# get ticket data my %Ticket = $Self->{TicketObject}->TicketGet(%Param);
my @ArticleBox = $Self->{TicketObject}->ArticleGet(TicketID => $Ticket{TicketID});
foreach my $Article (@ArticleBox) { if ($Self->InitArticleStorage(ArticleID => $Article->{ArticleID})) { my $Email = $Self->GetArticlePlain(ArticleID => $Article->{ArticleID}); if ($Email) { print "Ticket: ".$Ticket{TicketID}."/ Article: ".$Article->{ArticleID}."\n"; if ($Self->WriteArticlePlain(ArticleID => $Article->{ArticleID}, Email => $Email)) { print "article_plain of $Article->{ArticleID} written to fs\n"; # delete from db # $Self->{DBObject}->Do(SQL => "DELETE FROM article_plain WHERE article_id = $Article->{ArticleID}"); } else { print "could not write article_plain from $Article->{ArticleID} to fs\n"; } } if (my %AttachIndex = $Self->GetArticleAtmIndex(ArticleID => $Article->{ArticleID})) { foreach (keys %AttachIndex) { if (my %Attachments = $Self->GetArticleAttachment(ArticleID => $Article->{ArticleID}, FileName => $AttachIndex{$_})) { if ($Self->WriteArticlePart(Content => $Attachments{Content}, Filename => $AttachIndex{$_}, ContentType => $Attachments{ContentType}, ArticleID => $Article->{ArticleID})) { print "article_attachment of $Article->{ArticleID} written to fs\n"; # delete from db # $Self->{DBObject}->Do(SQL => "DELETE FROM article_attachment WHERE article_id = $Article->{ArticleID} AND filename = '$AttachIndex{$_}'"); } else { print "could not write article_attachment from $Article->{ArticleID} to fs\n"; } } } } } else { print "storage not initialized\n"; } }
return; } # -- sub InitArticleStorage { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(ArticleID)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } # ArticleDataDir $Self->{ArticleDataDir} = $Self->{ConfigObject}->Get('ArticleDir') || die "Got no ArticleDir!"; # get ArticleContentPath $Self->{DBObject}->Prepare(SQL => "SELECT content_path,incoming_time FROM article WHERE id = $Param{ArticleID}"); while (my @Row = $Self->{DBObject}->FetchrowArray()) { $Self->{ArticleContentPath} = $Row[0]; $Self->{Time} = $Row[1]; }
# check fs write permissions! my $Path = "$Self->{ArticleDataDir}/$Self->{ArticleContentPath}/check_permissons.$$"; if (-d $Path) { File::Path::rmtree([$Path]) || die "Can't remove $Path: $!\n"; } if (mkdir("$Self->{ArticleDataDir}/check_permissons_$$", 022)) { if (!rmdir("$Self->{ArticleDataDir}/check_permissons_$$")) { die "Can't remove $Self->{ArticleDataDir}/check_permissons_$$: $!\n"; } if (File::Path::mkpath([$Path], 0, 0775)) { File::Path::rmtree([$Path]) || die "Can't remove $Path: $!\n"; } } else { my $Error = $!; $Self->{LogObject}->Log( Priority => 'notice', Message => "Can't create $Self->{ArticleDataDir}/check_permissons_$$: $Error, ". "Try: \$OTRS_HOME/bin/SetPermissions.sh !", ); die "Error: Can't create $Self->{ArticleDataDir}/check_permissons_$$: $Error \n\n ". "Try: \$OTRS_HOME/bin/SetPermissions.sh !!!\n"; } return 1; } # -- sub WriteArticlePlain { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(ArticleID Email)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } # prepare/filter ArticleID $Param{ArticleID} = quotemeta($Param{ArticleID}); $Param{ArticleID} =~ s/\0//g; # define path my $Path = $Self->{ArticleDataDir}.'/'.$Self->{ArticleContentPath}.'/'.$Param{ArticleID}; # write article to fs 1:1 File::Path::mkpath([$Path], 0, 0775); # write article to fs if (open (DATA, "> $Path/plain.txt")) { print DATA $Param{Email}; close (DATA); # Set the mtime and ctime for the file to article.incoming_time utime($Self->{Time}, $Self->{Time}, "$Path/plain.txt"); return 1; } else { $Self->{LogObject}->Log( Priority => 'error', Message => "Can't write: $Path/plain.txt: $!", ); return; } } # -- sub WriteArticlePart { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(Content Filename ContentType ArticleID)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } # prepare/filter ArticleID $Param{ArticleID} = quotemeta($Param{ArticleID}); $Param{ArticleID} =~ s/\0//g; # define path $Param{Path} = $Self->{ArticleDataDir}.'/'.$Self->{ArticleContentPath}.'/'.$Param{ArticleID}; # check used name (we want just uniq names) my $NewFileName = decode_mimewords($Param{Filename}); my %UsedFile = (); my @Index = $Self->GetArticleAtmIndex( ArticleID => $Param{ArticleID}, ); $Param{Filename} = $NewFileName; # write attachment to backend if (! -d $Param{Path}) { if (! File::Path::mkpath([$Param{Path}], 0, 0775)) { $Self->{LogObject}->Log(Priority => 'error', Message => "Can't create $Param{Path}: $!"); return; } } # write attachment to fs if (open (DATA, "> $Param{Path}/$Param{Filename}")) { print DATA "$Param{ContentType}\n"; print DATA $Param{Content}; close (DATA); # Set the mtime and ctime for the file to article.incoming_time utime($Self->{Time}, $Self->{Time}, "$Param{Path}/$Param{Filename}"); return 1; } else { return; } } # -- sub GetArticlePlain { my $Self = shift; my %Param = @_; # check needed stuff if (!$Param{ArticleID}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need ArticleID!"); return; } # prepare/filter ArticleID $Param{ArticleID} = quotemeta($Param{ArticleID}); $Param{ArticleID} =~ s/\0//g; # open plain article my $Data = ''; my $SQL = "SELECT body FROM article_plain ". " WHERE ". " article_id = ".$Self->{DBObject}->Quote($Param{ArticleID}).""; $Self->{DBObject}->Prepare(SQL => $SQL); while (my @Row = $Self->{DBObject}->FetchrowArray()) { $Data = $Row[0]; } if ($Data) { return $Data; } else { return; } } # -- sub GetArticleAtmIndex { my $Self = shift; my %Param = @_; # check needed stuff if (!$Param{ArticleID}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need ArticleID!"); return; } my %Index = (); my $Counter = 0; my $SQL = "SELECT filename FROM article_attachment ". " WHERE ". " article_id = ".$Self->{DBObject}->Quote($Param{ArticleID})."". " ORDER BY id"; $Self->{DBObject}->Prepare(SQL => $SQL); while (my @Row = $Self->{DBObject}->FetchrowArray()) { $Counter++; $Index{$Counter} = $Row[0]; } return %Index; } # -- sub GetArticleAttachment { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(ArticleID FileName)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } # prepare/filter ArticleID $Param{ArticleID} = quotemeta($Param{ArticleID}); $Param{ArticleID} =~ s/\0//g; # get attachment index my %Index = $Self->GetArticleAtmIndex(ArticleID => $Param{ArticleID}); my %Data; my $Counter = 0; $Data{Filename} = $Index{$Param{FileName}}; my $SQL = "SELECT content_type, content FROM article_attachment ". " WHERE ". " article_id = ".$Self->{DBObject}->Quote($Param{ArticleID})."". " AND filename = '".$Param{FileName}."'"; $Self->{DBObject}->Prepare(SQL => $SQL); while (my @Row = $Self->{DBObject}->FetchrowArray()) { $Data{ContentType} = $Row[0]; # decode attachemnt if it's e. g. a postgresql backend!!! if (!$Self->{DBObject}->GetDatabaseFunction('DirectBlob')) { $Data{Content} = decode_base64($Row[1]); } else { $Data{Content} = $Row[1]; } } if ($Data{Content}) { return %Data; } else { return; } } # --
1;
------------------------------------------------------------------------
_______________________________________________ OTRS Mailingliste: otrs-de - Webpage: http://otrs.org/ Archiv: http://lists.otrs.org/pipermail/otrs-de/ Listenabo verwalten: http://lists.otrs.org/cgi-bin/listinfo/otrs-de/ Support oder Consulting fuer Ihr OTRS System? => http://www.otrs.com/
participants (3)
-
André Bauer
-
Dominic Priesmann
-
Robert Heinzmann