MySQL >= 8.x oder MariaDB – RegularExpression Word-Bounderies – Workaround

Ich hatte gerade ein echtes Aha-Erlebnis in Sachen Regular-Expressions. In denen bin ich sonst eigentlich recht fit, aber man lernt ja bekanntlich nie aus.

Folgende Konstrukte sind in einem SQL-Statement fehlgeschlagen, nachdem ich eine aktuelle MariaDB bzw. eine MySQL 8 Datenbank im Einsatz hatte.


[[:<:]]4711[[:>:]]

Selbst mir war diese Syntax in der Form nicht bekannt. Die beiden Pattern beziehen sich auf Word-Bounderies. Mit anderen Worten wir suchen ob in einem String die Folge 4711 enthalten ist, aber nur exakt diese, Folgen wie 47112 sollen nicht gefunden werden. Im konkreten Beispiel ging es um eine kommagetrennte Liste von Zahlen die zu durchsuchen ist. Dabei kann die 4711 aber auch am Anfang oder am Ende stehen. In MySQL 8 und höher ist der Unterstützung für diese Syntax weggefallen, aus welchem Grunde auch immer. Aber es gibt Ersatz:


[^[:word:]]4711[^[:word:]]

Auch hier suchen wir die Ziffernfolge umrandet von etwas was kein Wort ist – das kann ein Zeilenende, Zeilenanfang, oder etwas anderes als alphanumerisch + „_“ sein [:word:] == [:alphanum:_].

 

Domain Name Server mit PowerDNS – Schritt 1 – Konzept, Netzwerk und Authorative Server

Nachdem ich ja schon einiges in der letzten Zeit über DNS und Co geschrieben habe, geht es diesmal um eine konkrete Umsetzung. Lange Zeit habe ich schon einen eigenen DNS-Server betrieben, damals noch mit BIND. Mit den letzten Serverumzügen habe ich das dann der Einfachheit halber meinem Hosting-Anbieter überlassen (das richte ich ein, wenn der Rest läuft …). Wie das mit aufgeschobenen Aufgaben so ist, irgendwann wird der Status Quo zu einen „Dauerorium“ (die verstetigte Version eines Provisoriums).

Nun wollte ich es endlich angehen, und dabei auch einige zusätzliche Funktionen nachzurüsten. Unter anderem einen eigenen Ipv6 fähigen DynDNS-Service. Nach etwas Recherche stand ein grober Plan:

  • PowerDNS mit MySQL Backend
  • MySQL Server (läuft bereits, bekommt nur ein zusätzliches Schema)
  • Ein (Dyn)DNS-Frontend zur einfacheren Verwaltung und als HTTP(s) Schnittstelle zum Updaten
  • Microservice-Ansatz für die Einzelteile mit Docker als Paravirtualisierung
  • soweit möglich alles nativ in IPv6

Continue reading

Datenbankversionierung – Vor- und Nachteile bestimmter Praktiken

Gerade hatte ich mal wieder ein Schlüsselerlebnis in meiner Programmierung. In diesem Fall gab es ein Kollision mit YAML (YAML Ain’t Markup Language) – ich spreche explizit von Kollision, denn meine Erfahrung ist für den verwendeten Anwendungsfall einfach nur schlecht bzw. ich sehe leider aktuell einige Nachteile.

Datenbank-Schemata versionieren

Zur Versionierung von Datenbank-Änderungen bzw. um den Weg von einer „leeren“ Datenbank bis hin zum aktuellen Stand der Entwicklung zu beschreiben verwendet man in der Regel entsprechende Tools wie Liquibase oder Flyway. Das Prinzip ist im Prinzip immer das Gleiche – mit ein wenig „Rahmenwerk“ drum herum steuert man die Ausführung der Datenbank-Statements. Dabei kann man auf verschiedene Szenarien eingehen, einer der häufigst genannten ist, dass man ein Produkt mit verschiedenen Datenbank-Servern laufen lassen kann. Das ist beispielsweise wichtig wenn man Software an Firmen (seltener private Anwender) ausliefert, sich aber darauf einstellen muss, dass dort nicht unbedingt die aktuelle Version von MySQL läuft sondern eben im Zweifel auch ein PostgreSQL oder eine andere (kommerzielle) Datenbank wie Oracle. Continue reading

Fiese Falle in MySQL bei Verwendung von Views in (Left)-Joins

Da habe ich doch kurz vor Weihnachten noch eine echte Knobelaufgabe erlebt, deren Ergebnis für den ein oder anderen sicherlich interessant sein dürfte. Ausgangspunkt war folgendes eigentlich simple SQL-Statement:

Select * from tableA left join viewC 
     on tableA.id=viewC.lookup

Soweit so unspektakulär, auch wenn das Original noch mit einigen weiteren Filtern gespickt war und die View nicht gerade ein einfaches Query kappselt.

Was allerdings gar nicht lustig war, waren die Ergebnisse dieser Abfrage, denn sie entsprachen nicht den Erwartungen. Aus der View erhielt ich nämlich keinen Join-Partner obwohl ich diesen als Test extra geprüft hatte mit:

 Select * from viewC where lookup='TestValue'

Folgende Punkte habe ich als „übliche Verdächtige“ abgeprüft:

  • Vergleich der Datentypen – auch nicht die Ursache
  • Collations (also Zeichensätze) – auch mit Casting, das Ergebnis bleibt das Gleiche

Spannender Nebeneffekt, der bei der weiteren Untersuchung heraus kam – nutze ich die view nicht sondern packe 1:1 den Code aus der Definition mit in mein Statement

 
Select * from tableA left join 
   (select * from tableB 
     left join tableF 
         on tableB.id=tableF.foo 
      where tableF.bar='baz' and deletedDate is null)
   as testview on testview.id=tableA.id

Dann funktioniert wieder alles wie erwartet. Es staunt der Laie und der Fachmann wundert sich. Höchste Zeit das Problem genauer unter die Lupe zu nehmen und die bekannten Bugs von MySQL bzw. den Deviraten wie MariaDB und Percona zu durchforsten. Ergebnisse sind leider recht dürftig und beschreiben nicht das was ich erlebe. Immerhin stoße ich auf weitere Probleme die erst einmal völlig losgelöst von der Thematik erscheinen, es geht dabei um möglich Einstellungen des Servers und den Standard-Verhalten. Das hat sich in den letzten Jahren immer mal wieder etwas verändert und spiegelt des „Erwachsen werden“ von MySQL wieder. Wir erinnern uns: Es hat eine geraume Zeit gedauert bis MySQL endlich als Standard auf INNODB als Engine gewechselt hat und somit referenzielle Integrität in Form von Transaktionen und Foreign Keys unterstützt wurden. Ebenso aus der grauen Urzeit von MySQL stammen Probleme mit NULL-Werten bei Datumsfeldern (DATE,DATETIME). Und genau da liegt der Hase am Ende auch begraben. Die Server-Konfiguration gibt vor, dass der Server sich strikt verhalten soll, unter anderem bei den Datumsfeldern.
Somit ist es nicht mehr zulässig den „uralten NULL-Ersatz 0000-00-00 00:00:00“ zu verwenden. Die Routinen für den Ersatz kann man aber per Session einschalten, und genau das ist bei der manuellen Abfrage der Werte jedes mal passiert, sei es per manuellem Join oder bei der Abfrage der einzelnen Tabellen. Dabei verhält sich das isnull je nach eingestelltem Modus. Mal liefert es Zeilen mit „0000-00-00 00:00:00“ als Ergebnis, mal nicht, je nachdem ob strict-mode gesetzt ist oder nicht.

Warum läuft es dann beim Verwenden der View aus dem Ruder? Das liegt an der Art und Weise wie MySQL mit Views umgeht: Sind diese übermäßig komplex werden sie als separater Thread gestartet, der eine temporäre Tabelle bereitstellt auf die dann wiederum gejoined werden kann. Dieser Thread wird allerdings vom Serverprozess angestoßen, und dort steht der Modus per default auf strict und dieser wird dann auch angewandt. Somit ist das Ergebnis nicht das was ich bei der singulären Abfrage erhalten habe, sondern leider eben ein leeres da in der Tabelle anstelle NULL eben noch „0000-00-00 00:00:00“ verwendet wird.

Die temporäre Lösung ist, den Join manuell auszuführen, mittelfristig werden wir den Server umstellen. Das sind aber eigentlich nur kosmetische Lösungen. Die eigentlich korrekte Maßnahme für dieses Problem ist die Verwendung von sauberen NULL-Werten anstelle obskurer Workarounds. Leider verlassen sich sehr viele Applikationen bei uns noch auf die Tabelle und diese kruden Werte, einfach Umstellen ist also erstmal leider nicht drin. Ich kann jedem Entwickler nur wärmstens empfehlen, sich auf derartige Altlasten nicht zu verlassen und schnellstmöglich dafür zu sorgen diese abzubauen.

Mojibake – Buchstabensuppe

Na da hatte ich mir doch eine lange währende Aufgabe ausgesucht, die ich in der aktuellen Pflegephase meines Softwareprojekts endlich einmal angehen wollte. Aktuell läuft das Produkt soweit zufriedenstellend, nur an einigen kleiner Ecken knackt und knirscht es immer mal wieder. Unter anderem das leidige Thema „Sonderzeichen und deren Darstellung“ – da nicht absolut zwingend für die Funktionsfähigkeit habe ich das längere Zeit nicht großartig beachtet.

Nun gut, was ist das eigentliche Problem und woher kommt es? Die Ursachen liegen in der Historie des PCs ver- oder besser begraben: Da Speicher und Datenverarbeitung teuer war, hat man so sparsam wie möglich gearbeitet. Man stellte dabei fest: Inklusive aller wichtigen Zeichen der englischen Sprache und allem was man als Steuerzeichen (Zeilenumbruch, Tabulator, etc.) benötigt, bekommt man in 7 Bit unter. Das ist schon mal etwas krumm, denn gängig ist bei allem was Rechner betrifft doch eigentlich etwas eine Potenz von 2. Die ersten Rechner die ich verwendet habe (mit einem Intel 8088 als Unterbau) konnten nur 8 Bit parallel verarbeiten – immer mehr als die 7. Schon wenig später gab es 16Bit als Standard und mit der großen Verbreitung des PCs in die Büros war der Standard 32 Bit erreicht. Heute findet der sich bereits größtenteils in Ablösung durch 64Bit-Systeme. Was war der Sinn des 8. Bits? Man konnte es je nach Gusto verwenden: Entweder bohrte man damit den Zeichenvorrat auf, oder man konnte es zur Fehlererkennung und Korrektur einsetzen. Insgesamt muss man sagen: Alles mit Hand und Fuß.

Nur leider waren Rechner damals noch nicht gängigerweise vernetzt und die Standard-Definitoren etwas kurzsichtig – erst einmal wurde nur an die eigene Heimat gedacht, dass es möglicherweise andere Sprachen gibt, die nicht Englisch sind und einige Zeichen mehr mitbringen – wer konnte das schon ahnen? So kam was kommen musste: Einschränkungen bei diversen Dingen wie dem Betriebssystem: Umlaute oder Sonderzeichen in Dateinamen waren einfach nicht vorgesehen oder führten zu unvorhergesehenen Ergebnissen. Für das Verfassen und Verarbeiten von Texten war das natürlich keine Option, wer in Deutschland einen Text schreiben wollte, der sollte das auch tun können. Daher erweiterte man den Zeichensatz auf das 8 Bit und schon hatte man jede Menge Möglichkeiten auch Sonderzeichen abzuspeichern. Wichtig war nur, das jeder die gleiche Zuordnung traf und nicht einer ein ü für ein ö verwendete was die binäre Darstellung betraf. Soweit so gut oder auch schlecht.

Nun gehen leider bei verschiedenen Betrachtungen immer wieder einige Dinge durcheinander, auch ich habe hier am Anfag so meine liebe Mühe gehabt alles richtig zu verorten, daher hier einmal die Kurzfassung, zu allem was notwendig ist bzw. was sich auf die Darstellung und Handhabung von Texten am Rechner auswirken kann.

Binärer-Code

Eine Abfolge von 0en und 1en die irgendetwas darstellen – was ist für den Rechner erstmal unerheblich, dafür bedarf es einer Zuordnungs oder Code-Tabelle oder eines Algorithmus, der den Binärcode in etwas umsetzt, das dem Benutzer eher vertraut ist (leider sind wir Menschen nicht sonderlich gut binär veranlagt).

Code-Tabelle/Zuordnung/Algorithmus

Je nachdem was für eine Datentyp abgebildet werden soll, gibt es verschiedene Methoden dies zu tun. Am einfachsten sind ganze, positive Zahlen. Diese lassen sich aus der Abfolge von 0en und 1en entsprechend „errechnen“ – man muss nur noch wissen wo man anfangen muss (little endian vs. big endian). Für positive und negative Ganzzahlen hat sich das Zweierkomplement bewährt, für Fixkomma und Gleitkomma gibt es entsprechende Standards. Alles ein Thema für sich, denn wir wollen ja Texte bzw. Strings beleuchten. Hier hat sich eine Übersetzungstabelle als Mittel der Wahl erwiesen – sie ordnet jeder möglichen Abfolge von 0en und 1en eine menschenlesbare Bedeutung zu. So wird z.B aus der binären Folge 0110 1101  = 0x6D = O155 = „m“. Die Zuordnung an und für sich ist reine Willkür – man hat sich mal auf gewisse Standards geeinigt bzw. diese festgelegt, damit eben auf allen Rechner ein „m“ ein „m“ ist und nicht aus der „e-mail“ durch Willkür eine „e-nail“ wird. Der Urvater aller Standards dazu heißt ASCII (America Standard Code for Information Interchange) – wie der Name schon sagt ein nationales Standardformat. Auch bekannt ist diese Tabelle als Character-Set.

Windows-Codepages und Nornumg

Für verschiedene Regionen wurden verschiedene Verwendungen des verbliebenen 8. Bits (siehe oben) standardisiert – leider teilweise recht unterschiedlich, zum Teil nur an wenigen Stellen. Ziel war es, die in der jeweiligen Region verwendeten Sprachen möglichst gut abzubilden. Leider gab es da erst sehr spät eine Einigung auf einheitliche Standards, so dass zeitweise unterschiedliche Hersteller unterschiedliche Zuordnungen trafen. Ganz genauso wie wenn man Dateien ohne Angabe des verwendeten Zeichensatzes weitergab konnte es also passieren, dass der Zielrechner die Bit-Folge unterschiedlich interpretierte, weil bestimmte Bitfolgen eben etwas unterschiedliches bedeuteten. Dankenswerterweise hat man dabei die sichtbaren Zeichen (leider nicht so bei den Steuerzeichen) aus dem Urvater ASCII gleich belassen (das hatte sich ja bewährt, und für den internationalen Textaustausch reichte dieser kleinste gemeinsame Nenner auch aus). Gängig sind in der westlichen Welt die Standards der ISO 8859 in den Varianten 1-16, unter Windows die Codepage 1250 und unter DOS die Codepage 850.

Unicode

Mit der steigenden Internationalisierung und der immer stärker zunehmenden Vernetzung von Computern ist das mit den Nachschlagetabellen so eine Sache – man muss immer wieder etwas anderes berücksichtigen: Das Betriebsystem und die Region bzw. die Regionaleinstellungen des Anwenders – nur damit Text auch so ankommt wie er einegeben wurde. An einigen Stellen hat man sich beholfen oder die Probleme durch menschliche Intuition überbrückt – durch die Redundanz in der menschlichen Sprache kann man viele Worte ja auch lesen wenn Buchstaben vertauscht wurden oder nicht lesbar sind. Jeder Mensch der Lesen lernt kann das mehr oder weniger: Wenn aus einen Königreich dann plötzlich ein K$nigreich wird, dann wird der geneigte Leser das immer noch erkennen, auch wenn so ein Text etws mühsamer zu lesen ist. Ohnehin: wenn es international wird, dann ist die Sprache der Wahl in aller Regel doch Englisch und somit problemfrei. Aber wäre es nicht toll, wenn jemand eine Lösung hätte, damit ich auch chinesische Texte richtig angezeigt bekomme? Oder auch mal einen arabischen Text im Original anschauen? Das klingt weit hergeholt, aber diese Anwendungsfälle gibt es häufiger als man denkt. Die ursprüngliche Idee mit 32Bit und somit 4 Bytes pro codiertem Zeichen zu arbeiten (UTF-32) ist zwar einfach zu realisieren, aber ein wenig „over the top“ ist das schon: Wer überträgt schon gerne 3 komplett genullte Bytes wenn die Info doch nur in einem steht? Außerdem ist der Standard nicht abwärtskompatibel, er bricht mit den alten Vorgaben von ASCII, was bei älterer Software oder inkompatibler Software Ärger machen kann. Daher gibt es verschiedene Methoden mit variabler Anzahl von Zeichen, sozusagen das Beste aus beiden Welten: UTF-16 oder UTF-8 sind hierbei die bekanntesten Möglichkeiten.

Schriftarten und Glyphen

Wir haben uns nun langsam von der Bits & Bytes-Ebene nach oben gearbeitet hin zum abstrakten Begriff eines „characters“. Es gibt aber noch eine Ebene obendran – die Schriftarten oder auch Glyphen. Das ist im Prinzip eine weitere Tabelle die festlegt wie ein abstraktes Zeichen auf dem Ausgabemedium dargestellt werden soll. Jeder der sich einmal durch den Schriftenkatalog einer gängigen Office-Sammlung gewühlt hat, weiß das ein A ein A bleibt, auch wenn es etwas unterschiedlich geformt ist. Das beste Beispiel ist noch immer die eigene Handschrift im Vergleich zu einem beliebigen, gedruckten Werk. So lange eine gewisse Basis-Form erhalten bleibt, erkennt jeder Leser darin auch weiterhin ein A. Es gibt natürlich auch wieder Spezielle Schriftarten die als Darstellungvon Buchstaben Symbole haben, die nichts mit dem eigentlichen Zeichen zu tun haben – das bekannteste dürfte die Schriftarte Wingding oder auch Webdings sein. Besonders unpraktisch fällt mir diese immer wieder in e-mails aus Outlook auf, wenn man diese als Text ließt. Dort wird der Smily 🙂 automatisch in ein J umgewandelt und mit der Schriftart Wingdings formatiert – sieht zwar im ersten Moment richtig aus, aber mancher hat sich schon gewundert wo das „J“ denn nun herkommt. Ich merke es mir immer so: Ein Glyph macht aus einer Bit-Folge mittels einer Tabelle eine ganz bestimmte Grafik. Das ist zwar etwas vereinfacht, denn Schriftarten machen manchmal noch mehr, aber um den Überblick zu behalten reicht es allemal.

Collations

Was nochwas? Haben wir nicht alles endlich abgehandelt? Leider noch nicht ganz: Es gibt nämlich noch etwas was man mit Zeichenketten gerne macht: Aufreihen und Sortieren. Kling trivial, und es gibt doch Sortieralgorithmen wie Sand am Meer. Könnte man meinen, leider ist dem nicht ganz so (und das ist mit ein Grund weshalb man Sortieralgorithmen am besten an Zahlen erklärt …), denn auch wenn es im ersten Moment verlockend sein mag zu sagen: Man sortiert einfach nach der Größe der entsprechenden Ganzzahl der Bitfolge, das bringt leider nicht das gewünschte Ergebnis, denn schon bei ASCII haben wir ja große und kleine Buchstaben, ordnet man nun nach der errechneten Wertigkeit, so würde das folgende Sortierreihenfolge ergeben A,B,C…..,X,YZ,a,b,c – für die alphabetische Auflistung eines Materialkatalogs nicht das gewünschte Ergebnis – auch im Telefonbuch sucht man die Leute mit „von“ im Vornamen ja nicht unter „v“ sondern unter „V“ und dort irgendwo nach „Voldemort“…. und nun wirds ganz interessant: Wo ordne ich denn die Sonderzeichen wie Ä,Ö,Ü ein … sind das separate Zeichen am Ende? Oder soll ich sie behandeln wie „Ae“,“Oe“ und „Ue“? Das ganze nun noch auf die internationale Ebene gehoben und es wird ganz spannend: Je nach Land gibt es gleiche Zeichen, aber die werden unterschiedlich einsortiert, aber der Rechner soll es dennoch richtig machen. Das kann nur bedingt funktionieren, alles weitere regelt man über die Collation, die kann man zur Not auch bei der Sortierung mit angeben, und dem Sortierprogramm somit die Regeln vorgeben.

So jetzt habe ich einmal die Grundlagen zusammengefasst, die Auswirkungen und Mittel die man braucht um das alles umzusetzen behandle ich in einem separaten Artikel.

 

 

Immer wieder eine Freude – Mailserver einrichten

Neue Dinge machen bekanntlich in der Regel richtig Laune und Spaß – sei es neues Auto, neue Wohnung, neues (Männer-)Spielzeug. Natürlich habe ich mich daher auch über einen neuen Server auf Arbeit gefreut. Aber bekanntlich ist es bei einigen Dingen mit der Anschaffung bzw. Bestellung und Lieferung nicht getan. Die neue Wohnung will bezogen werden, das neue Auto eingeräumt etc. – genauso ist es mit einem Server, auch der wird zwar voreingerichtet geliefert, aber diverse Details und Stellschrauben muss man noch anpassen.

Die gängigen Services die auf einem Linux-Server sind in der Regel schnell eingerichtet, sei es ein Datenbank-Backend in Form von MySQL oder MariaDB, Apache als Webserver ist in der Regel auch gut paketiert, PHP als Standard-Glue-Language ebenso. Damit ist LAMP zumindest einmal abgehakt. Die Kür sind dann noch die Konfigurationen von Apache für verschiedene virtual Hosts (also mehrere Domains auf einer IP), und ggf. die notwendigen Extras für PHP (z.B. Imagick für die automatisierte Bildbearbeitung, diverse Klassen aus dem PEAR-Verzeichnis wie Tools zum Excel-Export) – alles nicht wirklich kompliziert.

Einziger Knackpunkt der mich jedesmal nervt ist die Einrichtung des Mailservers. Zwar funktioniert der Server im ersten Moment auch ohne, aber spätestens beim Versand von Systemnachrichten oder beim Aufruf der Mailfunktion aus PHP kommt man um einen Mailserver nicht oder nur schwerlich herum.

Warum ist das so? – Zum ersten gibt es nicht den Mailserverprozess an sich – wenn man es mit Windows vergleicht wäre eine solche Lösung wohl etwas in der Art wie Exchange, das aber weit mächtiger ist als ein reiner e-mail-Server. Vielmehr müssen für eine Mailserver wie ihn der Nutzer wahrnimmt verschiedene Räder ineinander greifen – leider nicht nur zwei sondern eine ganze Menge mehr.

E-mail – als erstes denkt man hier einmal an das altbekannte SMTP (Simple Mail Transfer Protocol) – wie bei allem wo „simple“ dransteht ist es das leider nicht. Ebenfalls spielen noch andere Protokolle eine wichtige Rolle: IMAP (Internet Message Access Protocol) und POP3 (Post Office Protocol 3). Allein für diese drei Protocolle ergeben sich schon mal mindestens drei Serverprozesse. Auf POP3 kann man evtl. heute im Zeitalter von Flatrates verzichten, allerdings bringen ettliche IMAP-Server auch gleich die POP3-Funktionalität mit, schaden kann es auf keinen Fall, auch wenn der Abruf über eine Wählverbindung eigentlich nur noch eine Nischenlösung ist.

Was macht da eigentlich was und warum gibts da verschiedenes, es geht doch um ein einzelnes „Produkt“ bzw. eine „Dienstleistung“. SMTP dient der Weitergabe von e-mails – viel mehr ist darin gar nicht spezifiziert. Eine e-mail wird zwischen verschiedenen System damit weiter gereicht bis sie ihren Bestimmungsort erreicht hat. Das kann durchaus einmal mehrere Schritte umfassen, nachverfolgen kann man es in den Headern der e-mail, die man nicht immer angezeigt bekommt, aber jedes bessere Mailprogramm hat dafür eine Option. Wie das Zielsystem mit der Mail umgeht ist ihm überlassen. Früher war es üblich pro Benutzer einfach eine Textdatei zu nehmen und die Mails dort hintereinader einzutragen. Das sogenannte MBox-Format, für wenige und reine Textmails eine praktikable Lösung, beim heutigen Volumen (Attachments) und dem parallelen Zugriff von mehreren Endgeräten nicht mehr so ganz aktuell, auch weil es keine Ordner-Struktur unterstützt (oder nur auf Umwegen, die zwar „akzeptiert“ aber nicht wirklich standardisiert sind). Durchgesetzt hat sich als Ersatz das Maildir-Format, wie der Name schon andeutet gibt es da Directories also Verzeichnisse. Ferner wird für jede e-mail eine separate Datei verwendet. Je nach Dateisystem ist das nicht unbedingt platzsparend, aber Speicherplatz ist heute ja in Hülle und Fülle vorhanden.

In den allerwenigsten Fällen ist das Zielsystem der e-mail gleich dem verwendeten Endgerät (schon allein aus Gründen der Erreichbarkeit – ein e-mail-Server ist 24h am Tag erreichbar, das Endgerät im Zweifel nicht). Daher gibt es die Protokolle IMAP und POP3 um e-mails vom Mailserver abrufen zu können. POP3 ist dabei an der klassischen Post orientiert: Man holt seine Nachrichten aus der Box und was man dann damit macht ist nicht mehr Sache des Servers (es sei denn man setzt spezielle Optionen) – der Vorteil: Es bedarf keiner ständigen Verbindung, Nachteil: Habe ich ein Smartphone, einen Laptop, einen Rechner und will womöglich noch per Webmail-Interface auf meine Mails zugreifen, wird die Synchronisation haarig bis unmöglich. IMAP ist daher Stand der Technik – die Nachrichten verbleiben auf dem Server, die meisten Clients haben aber einen Offline-Modus um die Nachrichten vorzuhalten, wenn gerade keine Verbindung zum Server möglich ist.  IMAP und POP3 kümmern sich also um die „letzte Meile“ des e-mail-Verkehrs. Daher haben diese Protokolle auch schon immer eine Benutzer-Authentifizierung vorgesehen, denn ein Mailserver hat ja in aller Regel multiple Postfächer. SMTP hatte das anfänglich nicht, und das ist eine echte Design-Schwäche, die unter anderem für eine e-mail-Plage namens SPAM mit verantwortlich ist.

Soweit so gut, wir haben also 3 Prozesse, das sollte sich doch machen lassen oder etwa nicht? Naja, ganz so einfach ist es heute leider nicht mehr: Im vorangegangenen Absatz habe ich bereits über Authentifizierung gesprochen, also Zugriffsbeschränkungen. Damit nicht jeder einfach SPAM verbreiten kann, sollte kein Mailserver irgendwelche Mails, die nicht für ihn bestimmt sind annehmen und weiterleiten (sogenanntes offenes Relay) – früher war das eine praktische Sache, aber heute ist es schon fahrlässig bis strafbar so etwas zu machen – jeder der sich selbst um den Mailserver kümmert weiß wie viel SPAM angelandet wird (bei mir ca. 95% aller Zustellversuche!). Nun gut, Benutzername und Passwort das ist ja gängig – nur diese Information müssen sich dann auch noch die drei Prozesse teilen und sie sollten nach Möglichkeit synchron laufen. Dafür kann man das Benutzerverwaltungs-System des Zielhosts heran ziehen, das ist der klassische Weg. Die Serverprozesse arbeiten dann mit den Passwort-Mechanismen des Betriebssystems zusammen. Für kleine Server sicherlich eine gute Möglichkeit, aber was wenn man mehrere Domains verwalten möchte, die unterschiedliche Nutzer haben? Für jeden auch noch ein Systemkonto anlegen (mit allen Vor- und Nachteilen) das wird irgendwann anstrengend und schwer zu warten ist es auch noch. Auf alle Fälle aber bedarf es also eines vierten Teils, der sich um die Authentifizierung kümmert, das kann PAM (Plugabble Authentification Module) sein, oder ein andere Mechanismus. Sind wir also bei 4 Prozessen, die man beachten muss. Nicht mehr schön aber noch überschaubar …

Lustig wird es erst bei weiteren Maßnahmen, die man heute aber leider treffen muss: SPAM-Abwehr und Virenschutz. Jede e-mail muss beim Eingang also überprüft werden, dazu gibt es verschiedene Mechanismen. SPAM bekämpft man klassischer Weise mit Spamassassin – ein recht ausgefeiltes (und wiederum modulares) System zur automatischen Inhaltsanalyse (z.B. Abfrage von Blacklists bekannter SPAM-Schleudern, Bayes-Filter und noch einiges mehr), für die Viren und Trojaner gibt es Virenscanner (so viele man möchte, bzw. soweit es der Server von der Leistung hergibt). Bewährt hat sich im Linux-Umfeld mittlerweile der OpenSource-Scanner ClamAV. Sind wir numher also bei 6 Teilen die man zusammensetzten muss, von der jeweiligen Einzelkonfig mal ganz abgesehen. Damit das Filtern leichter geht und auch eine gewisse Fehlerbehandlung (Virenscanner schmiert ab, Spamassissin hängt, etc.) zu erreichen, gibt es die Glue-Software „amavisd“. Macht in Summe schon einmal 7 Prozesse die es zu beherrschen gilt. MySQL bzw. Maria-DB kommt ggf. noch dazu wenn man die e-mail-Adressenverwaltung und ggf. auch die Speicherung der e-mails in einer Datenbank realisieren möchte.

Weiter kann man die Komplexität noch nach oben treiben, wenn man Verschlüsselte Verbindungen wünscht…. Insgesamt also doch ein recht umfangreicher Brocken nur für e-mail, das ja eigentlich bei einem Webserver „nur“ im Hintergrund mitlaufen soll. Die Einrichtung von Clients oder einem Webmail-Interface ist hingegen recht leicht wenn die Infrastruktur einmal steht. Diese stützen sich in aller Regel auf die oben genannten Protokolle und Schnittstellen. Damit der Post hier nicht zu lange wird, mache ich in der näheren Zukunft mal einen zu einer Konfiguration die ich am Laufen habe und mit der ich recht zufrieden bin.

 

 

Never touch a running system

Das hat unsere IT-Abteilung für die Versorgung mit Rechnern doch wieder mal exakt hinbekommen. Mein Kollege bekommt pünktlich zum Beginn meines Urlaubs einen neuen Rechner … Dabei habe ich doch gerade auch einen Praktikanten, den ich auch noch einweisen muss. Meine eigentliche Arbeit habe ich daher schon mal ganz hinten angestellt – vor dem Urlaub wird es wohl nichts mehr mit der Fertigstellung.

Insgesamt bin ich aber doch recht angetan von unserem Praktikanten – nach nicht mal zwei Wochen hat er jetzt ein einfaches Modul übernommen – ich bin mal gespannt wie sich das entwickelt während ich in Urlaub bin. Noch bin ich da recht zuversichtlich. Das da die eine oder andere Frage auftaucht, gerade am Anfang ist eine ganz natürliche Sache – jeder hat mal klein angefangen und da gibt es deutlich unselbstständigere Mitarbeiter.

Die größere Herausforderung war da der Rechner des Kollegen – da muss nämlich alles was Entwicklungstools heißt dann wieder neu installiert werden. Die werden nämlich nicht migriert – stattdessen gibt es neue Versionen von Notes und ein neues Office (in beiden findet man mal wieder nicht das was man eigentlich sucht …)

Besonders läßtig sind dann wieder die Spielchen mit WAMP und der Entwicklungsumgebung – Eclipse an sich ist ja noch harmlos – aber die ganzen Plugins sind dann schon wieder eine Herausforderung. Zumal man ja die Chance auch nutzt und dann doch mal endlich ein Upgrade auf die aktuelle Version machen will. Abgehangene Tools sind doch eher nicht so der Hit. Auch ich habe da wieder etwas dazu gelernt und meine internen Versionszähler mal aktulisiert. Mittlerweile ist Eclipse ja bei 4.2 aka Juno angekommen – ich arbeite noch mit Helios also der 3.6er Version – da wäre auch bei mir mal ein Upgrade angebracht … Immerhin habe ich jetzt auch endlich mal die Zeit gefunden mich mit den verschiedenen Konzepten etwas näher auseinander zu setzen – gut wenn man sein Werkzeug besser kennen lernt.

Subclipse hat sich auch weiter entwickelt – genauso wie Subversion selbst – das hatte ich noch so nebenher mitbekommen.

Insgesamt war ich dann doch überrascht wie gut das alles zu installieren war.

Das dicke Ende war dann mein spezieller Freund: Die Extensions für WAMP bzw. PHP – da gibt es dann wieder x Varianten und inkompatible Kompiler von Microsoft gleich en top mit dazu. Noch dazu, ganz wichtig für die Installation von Libraries unter Windows: Ein Neustart – und das für dynamische Libraries – was soll daran bitte dynamisch sein? Das hat mich mal wieder einiges an Nerven und Zeit gekostet – zumal es mal wieder so absolut unintuitiv ist ….

Morgen geht es noch ein wenig an weitere Plugins, dann haben wir es hoffentlich passend zum Urlaub geschafft …

 

Quick’n’Dirty in MySQL und anderen Datenbanken

Heute habe ich mich mal wieder einer Altlast der Datenbankentwicklung hingegeben, da sich einige Veränderungen ergeben hatten. Ich hatte schon mehrfach, die Hoffnung diese Tabellen der Datenbank endlich einmal längere Zeit in Ruhe lassen zu können um mich neuen Funktionen zu widmen – aber Pustekuchen wars.

Also wieder das Design auf den Prüfstand und schauen wie man es an die neuen Anforderungen anpassen kann. Ich weiß, dass ich vor etwa einem halben Jahr noch mit einem Freund und ausgesprochenen Experten in Sachen Datenbankdesign mich über einige Dinge ausgetauscht habe. An einigen Stellen hatte ich mich für geschickt gehalten bzw. wollte an dem Design nicht mehr übermäßig rütteln. Es hat ja auch alles soweit funktioniert und gerade die Schlüsseldefinitionen folgten auch einer gewissen Logik. Ich hatte mich für einen kombinierten Schlüssel entschieden – ein referenziertes Objekt kann zu einem Zeitpunkt (auf die Sekunde genau) nur an einer Stelle sein – für den Anwendungsfall eine absolut zutreffende Annahme. Zudem hatte ich den Schlüssel dann auch noch über mehrere Tabellen als Fremdschlüssel „durchgeschleift“ – vom damaligen Standpunkt aus war das eine mögliche Lösung die mir eigentlich auch gut gefiel – löste sie doch elegant auch diverse Bezugsprobleme, bzw. ich konnte einen Trigger verwenden um die notwendige Abhängigkeit einer Tabelle von einer anderen automatisch aufzulösen. Es gab also die Basis-Tabelle, eine erweiterte Tabelle und eine Tabelle die in vergleichsweise wenigen Fälle sich auf die erweiterte Tabelle stützte – ein klassisches Prozessgefälle – aus vielen kleinen Datensätzen werden am Ende nur wenige bis zur Blüte oder gar Reife gebracht.

Nun, die Anforderungen haben sich verschoben und die  mittlere/erweiterte Tabelle musste angepasst werden. Wie sich gezeigt hatte brauchten wir für eine spezielle Auswertung nicht nur eine Referenz auf die Basis-Tabelle sondern mindestens zwei, nach eingehender Analyse bin ich auf vier gekommen. Dies liegt in der Tatsache begründet, dass die erweiterte Tabelle eigentlich ein Zusammentreffen mehrer Datensätze aus der Basis-Tabelle abbildet. Das ist mir aber erst im Laufe der weiteren Entwicklung klar geworden – ich denke ich habe das auch beim letzten Mal eher „on the fly“,“mal eben schnell“, „quick’n’dirty“ entwickelt ohne die wahren Beziehungen zu erkennen. Was will man machen – so manches wird einem eben erst im Laufe der Zeit klar.

Erste Konsequenz – der ursprünglich ach so geschickte natürliche Schlüssel über zwei Spalten war nun nicht mehr tragbar – viel zu umständlich: für vier mögliche Referenzen wären es acht Spalten gewesen – Übersichtlichkeit gegen null, zumal die Aussagekraft der jeweiligen Schlüsselpaare zum Gesamtbild nur vergleichsweise wenig beiträgt.- und selbst wenn man es braucht – gejoint ist es dank Indizierung und Foreign Keys doch recht fix. Daher bekommt die Basis-Tabelle neben den natürlichen Spalten ein Surrogat – einen eindeutigen numerischen Primärschlüssel. Wie leicht der einem die Arbeit im weiteren macht ist mir bei der Anpassung des Programmcodes dann aufgefallen.

Wie mit der erweiterten, nunmehr ja eher aggregierenden Tabelle weiter verfahren – außer den vier Spalten für die Referenz – ein natürlicher Primärschlüssel über vier Felder schien mir doch recht gewagt, zumal diese Referenzen sich auch mal im Nachinein noch verändern können. Also auch hier die „künstliche“ Variante mit einem Surrogat.Das entschlackt auch die letzte Tabelle in der Reihe – deren Referenz musste ja auch wieder irgendwie hergestellt werden – nachdem der ursprünglich „durchgereichte“ Schlüssel ja nicht mehr da war musste da eh etwas neues her – auch hier erweist sich die Lösung per Surrogat doch recht tauglich.

Lehrwerte dieser Aktion:

Erstens – natürliche Schlüssel haben einen gewissen Charme – auch wenn sie zur Not aus zwei Spalten bestehen – moderne Datenbank-Systeme stecken das recht gut weg, auch was die Performance betrifft.

Zweitens – eine sorgfältige Analyse und Diskussion eines Entwurfs und die Bedeutung eines Objekts im Gesamtzusammenhang ist durch nichts zu ersetzen – leider zeigt sich hier mal wieder, dass es in meinem Fall keinerlei Prozessdefinition gab und somit natürlich auch die Artefakte nur sehr lückenhaft beschrieben waren. Ein Pflichtenheft wurde aus Kostengründen auch nicht erstellt – stattdessen gab es eine Alt-Datenbank an der man sich orientieren sollte – in bestimmten Dingen war das Design eine Anleitung „wie man es tunlichst nicht machen sollte“ (bei Gelegenheit werde ich dazu mal noch ein paar Zeilen schreiben). Auf einem solchen weichen Untergrund ein solides Fundament und hinterher ein Gebäude zu errichten ist nahezu unmöglich – irgendwo sackt es am Ende doch unangenehm weg.

Drittens – Surrogate sind im ersten Moment oftmals hinderlich und an einigen Stellen „verstellen“ sie teilweise den Blick aufs Wesentliche – man muss sich ggf. die weiterführenden Informationen aus anderen Tabellen erst mal zusammen suchen. Aber sie haben auch eine Menge Vorteile in Sachen Eindeutigkeit und Handhabbarkeit – wenn es einen eindeutigen Wert gibt, erleichtert dass das Auffinden eines Datensatzes und das Instanzieren eines Objekts daraus ganz erheblich.

Mal sehen welche alten Entscheidungen ich demnächst wieder ausgraben muss und mich über meine eigene Schusseligkeit wundern/ärgern darf. In diesem Sinne: Augen auf beim Datenbank-Design.

MySQL und der Null-Pointer

Da denkt man mal wieder an nichts Böses und prompt holt einen das 2. Gebot der C-Programmierung ein – obwohl man nicht in C programmiert, sondern eigentlich nur in einer Datenbank unterwegs ist.

Die 10 Gebote finden sich hier:

http://www.geekhideout.com/c-ten-commandments.shtml

Und die Aussage „Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.“  ist mir dabei mal wieder wie Schuppen von den Augen gefallen.

Was war passiert? Ich habe eine Datenbank-Tabelle, die mir angibt welche Subunternehmen an einer Arbeit mit beteiligt werden: Pro Arbeit kann es mehrere Subunternehmer geben, und jeder Subunternehmer kann an beliebig vielen Arbeiten mitwirken. Soweit so gut, ein klassischer Fall einer n:m Beziehung (wie man das im ER-Modell bezeichnen würde, UML ist da etwas genauer und würde in diesem Fall von einer 0..n:0..m Beziehung sprechen). Nun habe ich feststellen müssen: Selbst wenn das offiziell nur ein Subunternehmen ist, dann kann das immer noch recht groß ausfallen – die reine Angabe einer Firma als Referenz reicht also nicht unbedingt aus. Soweit ja kein Problem: Es gibt ja auch noch eine Tabelle aller am Projekt beteiligten Mitarbeiter – und ggf. ist einer davon als Ansprechpartner definiert. Leider nur ggf. und nicht zwingend – bei kleineren Firmen gibt es eine solche Zuweisung leider nicht.

Nun ja, was macht der geneigte Datenbank-Programmierer, genau, er macht das was er mal gelernt hat: Wenn es keine Referenz gibt, dann tragen wir den speziellen Wert „NULL“ ein. Selbst die referentielle Integrität kommt damit nicht aus dem Tritt – vielmehr ist es ein ganz klares Zeichen: Hier muss gar nicht erst nach einem passenden Partner in der anderen Tabelle gesucht werden – es gibt ihn schlichtweg nicht. Alles nicht richtig spannend und für mich bis dato kalter Kaffee.

Spannend wurde es im Zusammenhang mit der Modifikation der Datensätze: Um zu verhindern das Doppelte Einträge vorhanden sind, habe ich einen Unique-Key über die drei Spalten „Arbeit“,“Firma“,“Ansprechpartner“ angelegt. Zudem enthält die Tabelle noch ein paar zusätzliche Information, die aber nichts zu Sache tun. Was liegt also näher als mit einem „Insert ignore“, „Upsert“ bzw. dem praktischen MySQL-Konstrukt „Insert into …. on duplicate key update ….“  zu arbeiten? Das ist im Prinzip wie geschaffen für den beschriebenen Fall: Gibt es schon einen entsprechenden Eintrag in den  Schlüsselspalten, dann muss man nur noch die Satellitendaten entsprechend dem Bedarf aktualisieren (das lässt z.B. gut Luft für eine Überwachung durch wen und wann die letzte Änderung durchgeführt wurde).

Doch was ist das: Beim Testen der Funktion tauchen nach dem Speichern der Modifikation plötzlich Einträge doppelt und dreifach auf – also gerade nicht das was man erwartet hätte.

Erster Schritt in der Fehlersuche: Irgendwas im Code wurmig? – Nein, da stimmt alles und es gibt auch keine enstsprechenden Extra-Inserts – die Statements sehen so aus wie man sich das gewünscht hat. Nächster Schritt: Statements „manuell“ ausführen und … aha! – man kann Einträge bei denen der Ansprechpartner auf „NULL“ steht beliebig oft einfügen ohne dass die Schlüsselmechanismen ansprechen…. erster Verdacht: MySQL-Bug! – aber weit gefehlt: „It’s not a bug, it’s a feature“ zumindest in den Augen der MySQL-Entwickler … aber Abhilfe gibt es auch: einfach einen „primary“ anstelle eines „unique“ keys nehmen…. gesagt getan: Aber dann hagelts erst richtig: Denn in einem „Primary“-Key dürfen keine „NULL“-Werte auftauchen – was macht MySQL intern: Man behilft sich, und verändert sie Spalte – sie darf jetzt nicht „NULL“ enthalten und der Default-Wert ist „0“ – und das trotz eines Fremdschlüssels? – Und genau da beißt sich die Schlange dann in den Hintern … denn die bestehenden Daten sind komischerweise „valide“ – keine Schlüsselverletzung. Aber Einfügen geht nicht mehr: Will man „NULL“ einfügen sperrt sich die Tabellendefinition, will man „0“ verwenden, gibt es von Seiten der referentiellen Integrität auf die Finger … So ein Mist!

Lösungen gibt es bisher dafür keine 😐 zumindest nicht auf Datenbank-Ebene. Ich habe jetzt einen Hack-Around gemacht: Man lösche alle Einträge zur Arbeit und lege sie danach alle wieder an und schon kann man mit dem Unique-Key leben, man hat praktisch die Prüfung etwas weiter nach oben verlagert (wo sie in meinen Augen nicht hingehört).

Aber wie schon da Commandement von oben sagt: Null-Pointer sind ganz böse Dinge – und wenn man nicht aufpasst findet man sich in der Hölle wieder 😉