RRZE – Projekte & Prozesse (P&P)

Das Blog der RRZE Stabsstelle "Projekte & Prozesse"

Content

Queries über mehrere Datenbanken in PostgreSQL

English Version

Einleitung

Manchmal muss man Daten aus mehreren Datenbanken miteinander vergleichen, importieren oder aktualisieren. Wenn nicht gerade umständlich ein Programm mit mehreren Datenquellen geschrieben werden soll, bleiben einem nur die Tools der Datenbank selbst. Im Fall von PostgreSQL sind das das Kommandozeilen-Interface psql oder die GUI pgAdmin. Beide sind jedoch nicht in der Lage Abfragen über mehrere Datenbanken abzusetzen. Das liegt schlicht daran, dass beim Verbinden zu einer Datenbank dessen spezifischen Systemkataloge geladen werden und Postgres anscheinend nicht in der Lage ist mehrere dieser Art gleichzeitig zu verwalten. Es gibt allerdings einen Ausweg: dblink. Dabei handelt es sich um eine SQL-Bilbiothek, die mit Postgres ausgeliefert wird und per Datenbank installiert werden kann. Mit Hilfe dieser Bibliothek ist es möglich, Daten aus einer anderen Datenbank abzufragen und andere SQL-Statements zu dieser abzusetzen.

Installation

Die Installation von dblink ist einfach: die Bibliothek liegt im contrib-Ordner der PostgreSQL Installation und wird so in eine Datenbank installiert:

psql -U postgres -d -f `pg_config --sharedir`/contrib/dblink.sql

Das Installieren unter dem Benutzer postgres ist empfehlenswert, da hierbei alle Berechtigungen für etwaige zusätzliche Installationen vorhanden sind. Durch `pg_config --sharedir` findet die Shell das Share-Verzeichnis, in dem der contrib-Ordner liegt. Man sollte sich nur immer im Hinterkopf behalten, dass die Bibliotheken des contrib-Ordners per Datenbank installiert werden (und damit auch Teil eines vollständigen dump sind).

Verwendung

Für Abfragen aus anderen Datenbank muss die dblink-Funktion verwendet werden. Diese wird einfach in der FROM-Clause verwendet:

Select * From dblink('dbname=fauorgdb', 'select id, fauorgpath from orgunit') as t1(id bigint, fauorgpath text);
-- Beispiel auf anderer Postgres Instanz:
Select * From dblink('hostaddr=dbsrv42 dbname=fauorgdb user=fauorg password=drowssap', 'select id, fauorgpath from orgunit') as t1(id bigint, fauorgpath text);

Der erste Parameter der dblink-Funktion definiert die Datenbankverbindung (in diesem Fall lediglich den Datenbanknamen), der zweite Parameter enthält die Anfrage. Der Alias-Teil der FROM-Clause t1(id bigint, fauorgpath text) muss leider verwendet werden, weil Postgres das Schema der abgefragten Daten nicht kennt und sonst schimpft. Eine as-Deklaration ist zwar SQL-Standard, jedoch ist die Definition der Feldtypen eine Postgres-Erweiterung. Da die Verwendung von Abfragen auf einer anderen Datenbank jedoch nicht zur alltäglichen Arbeit gehört, empfiehlt sich die Definition von Views:

create or replace view provisionierte_daten as
select * from dblink('dbname=fauorgdb', 'select id from orgunit') as t1(id bigint);

Die View wird einmal definiert und fertig. Damit lassen sich dann Abfragen der folgenden Art definieren:

select * from provisionierte_daten
except
select "FAUORGId" from "IMP_FAUORG";

Das ist sehr nützlich, um zu prüfen, ob zwei Datenbanken den gleichen Datenbestand haben.

Ultraschnieker Anwendungsfall: selektives Replizieren ODER ‘PostgrESB’ (FT)

Die Kombination aus sehr mächtigem Regelsystem und der Fähigkeit auf entfernten Datenbanken DML-Statements abzusetzen, ermöglicht (selektives) Replizieren von Daten. Hierbei geht es darum eine gesamte Datenbank (oder Teile davon) auf eine andere Datenbank zu replizieren. Alles was man dafür tun muss, ist

  1. das gesamte oder partielle Schema der primären Datenbank auf einer sekundären Datenbank zu installieren
  2. für alle Tabllen und DML-Operationen (INSERT, UPDATE, DELETE) eine Regel wie folgt zu erstellen:
    CREATE OR REPLACE RULE replic_insert AS
      ON INSERT TO  DO 
        SELECT dblink_exec(
          'dbname=test', 
          'insert into  (id, name) values (' || quote_literal(new.id) || ',' || quote_literal(new.name) || ');
        );
    

Das Beispiel funktioniert für eine Tabelle mit den Feldern id und name. Leider muss das Statement recht kompliziert aufgebaut werden, da die sekundäre Tabelle mit den Pseudonamen NEW und OLD nichts anfangen kann. Wird die Regel darüberhinaus mit einer WHERE-Bedingung definiert, lässt sich die Replikation selektiv einschränken. Des Weiteren muss das Schema nicht notwendigerweise gleich sein, da auch heterogene Datenbanken mit Daten versorgt werden können – vorausgesetzt man kennt das Schema und kann die Regel dementsprechend definieren. Anders ausgedrückt ist also nicht nur die Replikation einer Datenbank möglich, sondern vielmehr die Provisionierung eines Teils des Datenbestands in eine andere PostgreSQL Instanz (um den Kreis zum Titel dieses Abschnitts abzuschließen).

Probleme

Probleme bei der Verwendung traten bei mir lediglich mit Passwörtern auf. Die ‘Fremddatenbank’ benötigt ja ebenfalls Authentifizierung. In meinen Tests wurden lediglich Datenbanken auf derselben lokalen Postgres-Instanz abgefragt und ich habe den lokalen Zugriff in der pg_hba.conf auf trust gesetzt:

# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
host    all         all         127.0.0.1/32          trust

Eine andere Möglichkeit besteht in der Verwendung der Passwortdatei.

Links

Querying multiple databases in PostgreSQL

Introduction

It is sometimes necessary to compare, import or update data from several databases. Unless you want to write a cumbersome program with several data sources, you are stuck with the database tools themselves. In PostgreSQL’s case, that is the command line interface psql or the GUI pgAdmin. Both of them are unable to process queries for several databases at once. The reason for this is simply that, upon connection to a database, its specific system catalogues are loaded, and Postgres seems unable to manage several of these at once. There is however, a way around this: dblink. This is an SQL library shipped with Postgres that can be installed via database. With this library, it is possible to query data from other databases and make SQL statements for these.

Installation

Installing dblink is easy: The library can be found in the PostgreSQL installation’s contrib folder. You can install it like this:

psql -U postgres -d -f `pg_config --sharedir`/contrib/dblink.sql

It is advisable to install as user postgres, as that gives you all the permissions necessary for possible additional installations. With `pg_config --sharedir`, the Shell finds the share directory that contains the contrib folder. Just keep in mind that the libraries within the contrib folder are installed via database (and are thus part of a complete dump).

Usage

For queries from other databases, you have to use the dblink function. You can use it with a simple FROM clause:

Select * From dblink('dbname=fauorgdb', 'select id, fauorgpath from orgunit') as t1(id bigint, fauorgpath text);
-- Example from another Postgres Instance:
Select * From dblink('hostaddr=dbsrv42 dbname=fauorgdb user=fauorg password=drowssap', 'select id, fauorgpath from orgunit') as t1(id bigint, fauorgpath text);

The dblink functions’s first parameter defines the database connection (in this case, the database name). The second parameter contains the query. The FROM clause’s alias part t1(id bigint, fauorgpath text) must be used because Postgres doesn’t know the queried data’s schema and starts whining otherwise. Although an as declaration would be SQL standard, definition of field types is part of the Postgres expansion. Since the use of queries in another database is probably not part of your everyday work, it is advisable to define Views:

create or replace view provisionierte_daten as
select * from dblink('dbname=fauorgdb', 'select id from orgunit') as t1(id bigint);

You only have to define the View once, and you are done. With this you can then use queries of the following kind:

select * from provisionierte_daten
except
select "FAUORGId" from "IMP_FAUORG";

This is very useful for checking if two databases have the same dataset.

Application deluxe: selective replicaion OR ‘PostgrESB’ (FT)

The combination of a very powerful set of rules and the possibility to put statements into remote databases, allows the selective replication of data. This means replicating a complete database (or parts thereof) into another database. All you have to do for this is

  1. installing the primary database’s complete or partial schema on the secondary database.
  2. create a rule for all tables and DML operations (INSERT, UPDATE, DELETE) as follows:
    CREATE OR REPLACE RULE replic_insert AS
      ON INSERT TO  DO 
        SELECT dblink_exec(
          'dbname=test', 
          'insert into  (id, name) values (' || quote_literal(new.id) || ',' || quote_literal(new.name) || ');
        );
    

The example works with a table containing the fields id and name. The statement needs to be rather complicated, as the secondary table cannot interprete the pseudo names NEW and OLD. If the rule is futher defined with a WHERE clause, the replication can be made very selective. In addition, the schemata don’t need to be identical, as you can also provide heterogenical databases with data – if you know the schema and can define your rule accordingly. In other words, this allows not only replication of a database, but also provisioning part of your dataset into another PostgreSQL instance.

Problems

The only problems I found were with passwords. The ‘foreign database’ needs authentication as well. In my tests, I only queried local databases within the same Postgres instance, and set the local access in pg_hba.conf to trust:

# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
host    all         all         127.0.0.1/32          trust

Another possibility is the use of a password file.

Links

Inkscape und Datenschutz jetzt auch mit dem RRZE Icon Inspector

English Version

An den letzten Artikel anknüpfend kann nun auch der RRZE Icon Inspector die etwas zu gesprächigen XML Tags aus den Inkscape SVGs entfernen.

Nach Vorlage von Sebastian Pippings svgstrip werden bei jedem Lauf die Attribute ‘sodipodi:absref’, ‘sodipodi:docbase’, ‘sodipodi:docname’ im SVG-Tag, sowie das Attribut ‘inkscape:export-filename’ aus allen Tags entfernt.

Datenschutz wieder hergestellt 😉

Inkscape and Data security with the RRZE Icon Inspector

Picking up where the last article left off, the RRZE Icon Inspector is now able to remove the rather “talkative” XML Tags from Inkscape’s SVG files.

Using Sebastian Pippings svgstrip template, attributes ‘sodipodi:absref’, ‘sodipodi:docbase’, ‘sodipodi:docname’ are removed from the SVG-Tag, and attribute ‘inkscape:export-filename’ is removed from all tags..

Welcome back, Data security! 😉

Isoliertes Classloading auf dem JBOSS AS

English Version

Durch Verwendung von Technologien wie Enterprise Java Beans (EJB) und Enterprise Service Bus (ESB) ist es heute ohne Weiteres möglich, lose gekoppelte und feingranular modulare Dienste zu entwerfen, die in Kollaboration eine verteilte Anwendung ausmachen. Einheiten des Entwurfs sind nicht mehr ganze Anwendungen, sondern klar abgegrenzte Dienste, die lokal oder entfernt benutzt werden können. Diese Dienste werden auf ein oder mehreren Applikationsservern (AS) deployed und können dort benutzt werden. Wenn die Dienste selbst auf Funktionen aus Bibliotheken zugreifen müssen, so kann es passieren, dass zwei unterschiedliche Dienste dieselbe Bibliothek in unterschiedlichen Versionen verwenden (müssen). Da beim Standard-Deployment allerdings alles von einem Classloader geladen und instanziert wird, tritt das Problem auf, dass nicht genau vorherzusagen ist, welche der beiden Versionen ein und derselben Bibliothek zuerst geladen wird. Darüberhinaus ist es ja gerade notwendig beide Versionen vorzuhalten, da sie in unterschiedlichen Contexten verwendet werden. Dies ist ohne weiteres Zutun nicht möglich. Daher bietet der JBOSS AS die Möglichkeit, Einheiten des Deployments (EJBs oder ESBs) so zu konfigurieren, dass sie von einem isolierten Classloader geladen werden. Damit ist es möglich die gleiche Bibliothek mehrfach zu laden (insbesondere mit unterschiedlichen Versionen), wenn dies gewünscht wird.

Eine Beispielkonfiguration für ESBs ist im Folgenden dargestellt:

[deployment.xml]
<jbossesb-deployment>
	<loader-repository>
		fauorg.esb.loader:loader=simple-scoped
		<loader-repository-config>java2ParentDelegaton=false</loader-repository-config>
	</loader-repository>
</jbossesb-deployment>

Die Datei sollte deployment.xml heißen und unter src/main/resources/META-INF abgelegt werden. Sie sorgt dafür, dass Klassen in folgender Reihenfolge geladen werden:

  1. /lib/ (aus dem verpackten ESBs)
  2. server/default/lib/

Dabei werden also lediglich Klassen aus dem server/default/lib/ Verzeichnis geladen, die nicht mit verpackt wurden. Der Name des JMX Classloader Objekts ist im Beispiel fauorg.esb.loader und sollte vom Entwickler festgelegt werden. Damit lassen sich auch mehrere Einheiten des Deployments in einem Classloader zusammenfassen.

Nähere Informationen hierzu finden sich hier. Eine Zusammenstellung von UseCases zum Thema isoliertes Classloading auf dem JBOSS AS gibts hier. Zum Aktivieren eines Loggers für den Classloader verfolgt man am besten diese Anleitung.

Isolated loading of classes on JBOSS AS

Using techology like Enterprise Java Beans (EJB) and Enterprise Service Bus (ESB), it is easily possible to design granular, losely connected modular services. In collaboration, these create distributed applications. Design units are no longer complete applications, but clearly separated services that can be used locally or remotely. These services are deployed on one ore more application servers (AS), and can be used there. If the services themselves need to access libraries, it is possible for two different services to use the same library in different versions. Since in standard deployment, everything is loaded and instanced by a classloader, the problem can occur that it becomes impossible to predict which library will be loaded first. It is furthermore necessary to keep both versions available, since they are used in different contexts. Without additional information, that is not possible. That is why JBOSS AS offers the possibility of configuring deployment units (EJBs or ESBs) in such a way that they are loaded by an isolated classloader. That makes it possible to load the same library multiple times (especially in different versions) if that is necessary.

Here is an exemplary configuration for ESBs:

[deployment.xml]
<jbossesb-deployment>
	<loader-repository>
		fauorg.esb.loader:loader=simple-scoped
		<loader-repository-config>java2ParentDelegaton=false</loader-repository-config>
	</loader-repository>
</jbossesb-deployment>

The file should be named deployment.xml and put in src/main/resources/META-INF. It ensures that classes are loaded in the following order:

  1. /lib/ (from the packaged ESBs)
  2. server/default/lib/

That means that only classes from the directory server/default/lib/ that weren’t packaged are loaded. The name of the JMX Classloader Object can be found in example fauorg.esb.loader and should be defined by the developer. This allows for several deployment units to be combined into one classloader

You can find further information here. A summary of UseCases regarding isolated classloading on the JBOSS AS can be found here. To activate logging for the classloader, follow these instructions.

Hibernate und Case-sensitive Tabellen-/Spaltennamen

English Version

Wenn Hibernate so konfiguriert ist, dass es das relationale Schema der Datenbank selbst erzeugt, werden alle Tabellen- und Spaltennamen derart erzeugt, dass sie lediglich aus Kleinbuchstaben bestehen. Das ist meines Erachtens auch sinnvoll, weil durch die Verwendung von Hibernate das zugrundeliegende Datenbankmanagementsystem einfach ausgetauscht werden kann. Einige Datenbanksysteme sind jedoch case-sensitiv und würden bei Verwendung einer ‘beliebigen’ Schreibweise Fehler erzeugen. Verfolgt man daher die Konvention alles klein zu schreiben, entstehen diese Probleme gar nicht. Des Weiteren ist es nicht empfehlenswert Tabellen- oder Spaltennamen zu vergeben, die sich einzig durch ihre Verwendung von Groß- und Kleinbuchstaben unterscheiden. Über die bessere Lesbarkeit von Tabellen- oder Spaltennamen mit gemischter Schreibweise (beispielsweise durch Verwendung von Binnenmajuskeln) lässt sich ohnehin streiten.

Hat sich ein Datenbankadministrator dennoch dafür entschieden Großbuchstaben in Bezeichnern eines relationalen Schemas zu verwenden, oder liegt das Datenbankschema schlicht bereits vor, wenn Klassen auf Relationen gemappt werden, kommt man nicht umher den Namen des Feldes oder der Tabelle explizit zu definieren. Ohne diese Definition würde Hibernate die Namen der Membervariablen und Klassen vollständig klein geschrieben verwenden und Datenbanksysteme, die Groß- und Kleinschreibung unterscheiden, würden Fehler erzeugen. Um den Namen des Bezeichners für die Datenbank zu definieren muss man lediglich den name-Parameter der Annotationen @Table und @Column (beide aus javax.persistence) verwenden und den vergebenen Namen in Backticks (`) setzen:

@Entity
@Table(name="`MyEntity`")
public class MyEntity {

  @Column(name="`StartDatum`")
  protected Date startDatum;

  ...
}

Das Mapping verwendet also eine Tabelle namens MyEntity, in der sich ein Feld namens StartDatum befindet.

Dank gilt Frank Tröger, der diesen Trick gefunden hat. Sollte man bei der Verwendung von Bezeichnern, die gleichzeitig Schlüsselwörter der Sprache SQL sind, Probleme haben, könnte die Verwendung von eckigen Klammern helfen.

Hibernate and case sensitive table and column names

If Hibernate is configured to create the relational database schema itself, all table and column names created will be lower case. I think this is reasonable in most cases, as you can simply switch the underlying database management system when using Hibernate. Several database systems are case sensitive, however, and would generate errors when using an ‘arbitrary’ notation. If you keep to the standard of writing everything lowercase, these problems won’t turn up. It is also not advisable to chose table or column names that only differ in their use of lower or uppercase letters. Whether or not table and column names with mixed notation (e.g. when using Camel Case) are better to read is open to discussion, anyway.

If the database admin decided to use upper case characters in relational schema names, or when the database schema is already existent when classes are mapped to relations, you won’t get around an explicit definition of fields or tables. Without this definition, Hibernate will use lower case for all member variables and classes, causing database system that differentiate between lower and uppercase to generate errors. To create a definition for a designator, simply use the name parameter of annotations @Table and @Column (both from javax.persistence) and put the name in backticks (`):

@Entity
@Table(name="`MyEntity`")
public class MyEntity {

  @Column(name="`StartDatum`")
  protected Date startDatum;

  ...
}

Mapping thus uses a table called MyEntity, with a field called StartDatum.

Thanks go to Frank Tröger, who found this trick. If you have problems using designators that are SQL key words, the use of square brackets might help.