PostgreSQL – Sebastians Blog https://sgaul.de Neues aus den Softwareminen Mon, 04 Mar 2019 13:01:24 +0000 de-DE hourly 1 https://wordpress.org/?v=6.1.1 https://sgaul.de/wp-content/uploads/2019/02/cropped-sgaul-2-1-32x32.jpg PostgreSQL – Sebastians Blog https://sgaul.de 32 32 Vollständige Dependent-Einstellungen in Rails-Models testen https://sgaul.de/2019/03/04/vollstaendige-dependent-einstellungen-in-rails-models-testen/ Mon, 04 Mar 2019 15:38:13 +0000 https://sgaul.de/?p=2933 Vollständige Dependent-Einstellungen in Rails-Models testen weiterlesen]]> Ein selten, aber leider regelmäßig wiederkehrendes Problem sind Fremdschlüsselbeziehungen beim Löschen. In Rails muss auf Seite des Schlüsselziels definiert werden, ob ein Fremdschlüssel auf null gesetzt werden darf oder ob das ganze Model gelöscht werden muss. Vergisst man diese Konfiguration wirft die Datenbank beim Löschen einen Fehler. Um dies zu vermeiden möchte ich alle Fremdschlüssel, die auf eine Model-Tabelle zeigen, überprüfen, ob sie entsprechend konfiguriert sind. Hierfür verwende ich ein shared Example in Rspec.

Fremdschlüssel finden

Die Schlüssel für das beschriebene Model lassen sich in zumindest mit Postgres folgendermaßen auflisten:

def foreign_keys(to_table)
  ActiveRecord::Base.connection.tables.map do |table_name|
    ActiveRecord::Base.connection.foreign_keys(table_name).select{ |index| index.to_table == to_table }
  end.flatten
end

Konfigurierte Assoziationen finden

Die Assoziationen eines Models finden wir mit folgendem Ansatz. Wir ignorieren Assoziationen mit Scopes, da hier nicht ohne weiteres festgestellt werden kann, ob diese alle möglichen Verbindungen abdecken. Auch beschränken wir uns auf Has-many- und Has-one-Beziehungen, da diese zumindest aktuell die einzig relevanten sind. Sollte hier etwas fehlen ist dies auch nicht weiter tragisch, da der Test im Zweifel lieber zu viel als zu wenig bemängeln soll.

def safe_associations(target_class)
  associations =
    target_class.reflect_on_all_associations(:has_many) +
    target_class.reflect_on_all_associations(:has_one)
  associations.select{ |assoc| assoc.options[:dependent].in?(%i{destroy nullify}) && assoc.scope.nil? }
end

Shared Example für verdächtige Keys

Nun können die gültigen Konfigurationen von den Fremdschlüsseln abziehen. Nur wenn die Differenz leer ist, sind alle Schlüssel konfiguriert. Da wir bei Scopes etwas übervorsichtig sind, erlauben wir aber auch eine ignore-Option, um bestimmte Fremdschlüssel zu ignorieren:

shared_examples 'a dependent model' do  |options = {}|
  it 'has a dependent option for has_* associations' do
    foreign_keys = foreign_keys(described_class.table_name)
    associations = safe_associations(described_class)
    ignore = options[:ignore] || []

    suspicious_keys = 
      foreign_keys.map{ |key| "#{key.from_table}.#{key.column}" } -
      associations.map{ |assoc| "#{assoc.klass.table_name}.#{assoc.foreign_key}" } -
      ignore

    expect(suspicious_keys).to be_empty
  end

  # ...
end

Nun noch die Fehlermeldungen modifizieren und etwas Doku ergänzen und schon kann das Example in Model-Specs eingebunden werden:

describe BookWithChapters do
  it_behaves_like 'a dependent model'
end
]]>
Datenbanken im Home-Verzeichnis speichern https://sgaul.de/2015/01/18/datenbanken-im-home-verzeichnis-speichern/ Sun, 18 Jan 2015 16:44:35 +0000 https://sgaul.de/?p=2725 Datenbanken im Home-Verzeichnis speichern weiterlesen]]> Mit PostgreSQL ist es sehr einfach einen Datenbank-Cluster im Home-Verzeichnis eines Nutzers anzulegen. Dies kann sehr hilfreich sein, wenn beispielsweise nur das Home-Verzeichnis verschlüsselt wird und die Datenbank nicht offen herumliegen soll.

Unter Ubuntu 14.10 installieren wir Postgre und betrachten das automatisch angelegte Standard-Cluster:

sudo apt-get install postgresql

pg_lsclusters
Ver Cluster Port Status Owner Data directory Log file
9.3 main 5432 online postgres /var/lib/postgresql/9.3/main /var/log/postgresql/postgresql-9.3-main.log

Es gehört dem Nutzer postgres und ist unter /var/lib beheimatet. Beides ist auf meiner Entwicklungsmachine nicht erste Wahl. Ich droppe daher den Cluster und erstelle einen neuen, den ich meinem eigenen Nutzer zuordne:

sudo pg_dropcluster --stop 9.3 main
sudo pg_createcluster --user sg -d /home/sg/.postgresqldata 9.3 main

pg_lsclusters
Ver Cluster Port Status Owner Data directory Log file
9.3 main 5432 online sg /home/sg/.postgresqldata /var/log/postgresql/postgresql-9.3-main.log

Abschließend muss die Datenbank ggf. noch gestartet werden und man kann mit dem Standardnutzer ganz normal arbeiten:

sudo service postgresql start
createuser -dPER my-project-db-user
]]>
RSpec und PostgreSQL: Insufficient Privilege https://sgaul.de/2013/09/17/rspec-und-postgresql-insufficient-privilege/ Tue, 17 Sep 2013 15:01:43 +0000 https://sgaul.de/?p=2352 RSpec und PostgreSQL: Insufficient Privilege weiterlesen]]> Bei einem rake spec erhielt ich den folgenden Fehler:

PG::InsufficientPrivilege: ERROR:  permission denied to create database
[...]

Es mag eine falsche Einstellung sein (ein einfaches rspec verursacht das Problem nicht), aber offensichtlich benötigt der Datenbanknutzer in meiner Konfiguration das Recht, neue Datenbanken zu erstellen. Auf meiner lokalen Maschine, wo die Tests laufen, kann ich das verantworten.

Postgres-Kommandozeile aufrufen:

sudo su - postgres
psql

Recht zum Erstellen der Datenbank an Nutzer vergeben:

ALTER USER "my-db-user" CREATEDB;
]]>
SQL-Datenbankänderungen testen https://sgaul.de/2013/04/14/sql-datenbankanderungen-testen/ Sun, 14 Apr 2013 16:53:11 +0000 https://sgaul.de/?p=2128 SQL-Datenbankänderungen testen weiterlesen]]> Manchmal dauert es ein wenig, bis man auf das Offensichtliche kommt. Ich habe hin und her überlegt, wie ich ein Update und Alter Table testen kann, ohne die Daten zu gefährden. Die Lösung ist elementarer Bestandteil einer jeden SQL-Datenbank: Eine Transaktion.

Transaktionen in PostgreSQL

begin;
alter table mytable rename column id to name;
\d mytable

         Table "public.mytable"
   Column   |   Type     |   Modifiers   
------------+------------+--------------
 name       | bigint     | not null
rollback;
\d
         Table "public.mytable"
   Column   |   Type     |   Modifiers   
------------+------------+--------------
 id         | bigint     | not null

Sind die vorgenommenen Änderungen korrekt, können sie mit commit; übernommen werden. Der offizielle SQL-Befehl zum Starten einer Transaktion ist start transaction;. Allerdings unterstützen alle mir bekannten Datenbanken die etwas handlichere Form begin;.

Verschachtelte Transaktionen: Savepoints

Ist der Test komplexer, so dass man Transaktionen schachteln möchte, kann man sogenannte Savepoints verwenden.

begin;
-- Änderung 1
savepoint sp1;
-- Änderung 2a
rollback to savepoint sp1; -- 2a zurücknehmen
-- Änderung 2b
rollback; -- 2b und 1 zurücknehmen

Transaktionen in MySQL

Die PostgreSQL-Beispiele werden auch von MySQL unterstützt.

]]>
PostgreSQL-Byte-Array mit PHP in Datei schreiben https://sgaul.de/2012/10/20/postgresql-byte-array-mit-php-in-datei-schreiben/ Sat, 20 Oct 2012 16:42:30 +0000 https://sgaul.de/?p=1464 PostgreSQL-Byte-Array mit PHP in Datei schreiben weiterlesen]]> Will man Binärdaten, die in einer PostgreSQL-Tabelle als Typ bytea hinterlegt sind, direkt ausgeben oder in eine Datei schreiben, muss die Rückgabe zunächst demaskiert werden. Andernfalls wird sich euer Bildbetrachter weigern, eine gespeicherte Grafik zu öffnen. Grund dafür ist, dass die Daten bei der Auswahl mit SELECT als Oktalzahlen zurückgegeben werden, die mit einem Backslash beginnen. Die Funktion pg_unescape_bytea entfernt diese.

$result = pg_query("SELECT $rowName FROM $tableName;");
while ($row = pg_fetch_assoc($result)) {
    $fileHandler = fopen($filePath, 'wb');
    fwrite($fileHandler, pg_unescape_bytea($row[$rowName]));
    fclose($fileHandler);
}
]]>