Der Befehl postgres client copy (\ copy) hat keinen Zugriff auf eine temporäre Tabelle?

8

Ich generiere eine Liste von SQL-Befehlen, um einige Daten zu exportieren, die ich letztendlich mit psql -f ausführe. Die Abfragen erhalten alle dieselbe Teilmenge von Daten, daher dachte ich, ich würde die Qualifikationen herausrechnen und eine Liste der zulässigen Benutzer-IDs in eine temporäre Tabelle wie diese einfügen

create temporary table tmp_export_users as (select id from users where ...)

dann verweise darauf in meinen \ copy Befehlen wie

\copy (select ... from table where user_id in (select id from tmp_export_users)) TO 'filename.csv' WITH CSV HEADER

Diese befinden sich alle in derselben Datei, eine pro Zeile, und führen sie aus. Wenn ich die Fehlermeldung erhalte, dass die Kopierbefehle die temporäre Tabelle nicht sehen können, schätze ich, dass der Client-Kopierbefehl nicht dieselben Postgres verwenden darf Sitzung als psql.

Ist das korrekt? Gibt es eine Möglichkeit, dieses Verhalten zu ändern?

jkebinger
quelle

Antworten:

16

\copy kann eine temporäre Tabelle verwenden.

Zuerst habe ich dies mit Version 9.0 in der Kommandozeile getestet und bestätigt .
Dann habe ich eine Datei mit dem Meta-Befehl SQL und psql unter \copyVerwendung mehrerer temporärer Tabellen erstellt. Das hat auch bei mir funktioniert.

CREATE TEMP TABLE tmp as SELECT * FROM tbl;
\copy (SELECT * FROM tmp JOIN tbl USING (id)) TO '/var/lib/postgres/test1.csv';

Anruf:

psql -p5432 mydb -f test.sql

Beachten Sie das abschließende Semikolon, das am Ende einer Datei optional ist (implizit beendet), aber nach jeder anderen SQL-Anweisung und auch nach der letzten erforderlich ist, wenn es interaktiv in psql ausgeführt wird.

Normalerweise können psql-Meta-Befehle nicht mit SQL in derselben Zeile in einer Datei gemischt werden, die per ausgeführt wird psql -f. Ich zitiere das Handbuch auf psql :

Das Parsen nach Argumenten endet am Ende der Zeile oder wenn ein anderer nicht zitierter Backslash gefunden wird. Ein nicht zitierter Backslash wird als Beginn eines neuen Meta-Befehls verwendet. Die spezielle Sequenz \\(zwei Backslashes) markiert das Ende der Argumente und setzt das Parsen von SQL-Befehlen fort, falls vorhanden. Auf diese Weise können SQL- und psql-Befehle in einer Zeile frei gemischt werden. In jedem Fall können die Argumente eines Meta-Befehls jedoch nicht über das Zeilenende hinaus fortgesetzt werden.

Unterschiedliche Regeln gelten nach \copy , though. Im Wesentlichen wechselt psql nach \copySee: automatisch in den SQL-Modus zurück :

Aber Sie haben geschrieben, Sie hätten alle Befehle in getrennten Zeilen. Das kann also nicht die Erklärung in Ihrem Fall sein.


Abgesehen davon haben Sie darüber nachgedacht, COPY(den SQL-Befehl ) anstelle von \copy(dem psql-Meta-Befehl ) zu verwenden?

Natürlich müsste die Zieldatei lokal auf dem Server sein, in diesem Fall nicht auf dem Client. Es gelten unterschiedliche Dateiberechtigungen. Das Handbuch :

In einem COPYBefehl genannte Dateien werden direkt vom Server gelesen oder geschrieben, nicht von der Clientanwendung. Daher müssen sie sich auf dem Datenbankserver befinden und für diesen zugänglich sein, nicht auf dem Client. Sie müssen für den PostgreSQL-Benutzer (die Benutzer-ID, unter der der Server ausgeführt wird) und nicht für den Client zugänglich und lesbar oder beschreibbar sein.

Erwin Brandstetter
quelle
copy wird als postgres-Benutzer ausgeführt. \ copy schließt die Kopie ein, um sie in std out zu schreiben und in die Datei umzuleiten, an die Sie sie senden. Sie können auch psql aufrufen, \ o verwenden, um die Ausgabe an eine Datei zu senden, und dann eine Kopie an stdout ausführen, um einen ähnlichen Effekt zu erzielen.
Scott Marlowe
Um sicher zu gehen, habe ich den Test in meiner Antwort mit einem Superuser (postgres) und einem Dummy-Benutzer ausgeführt. Beide arbeiten für mich. Gleiche Ergebnisse auf v8.4.
Erwin Brandstetter
1
Ja, ob der Postgres-Unix-Benutzer auf Dinge wie / tmp zugreifen kann oder nicht, hängt davon ab, ob SELinux installiert ist oder nicht und ob es sofort einsatzbereit ist. Die \ copy oder copy to stdout sind definitiv die zwei zuverlässigeren Möglichkeiten, copy zu verwenden.
Scott Marlowe
1
Vielen Dank für die Antworten an alle. Es sieht so aus, als hätte ich es versäumt, die Zeile, die eine temporäre Tabelle erstellt hat, mit einem Semikolon zu beenden, sodass sie nicht erstellt wurde. Jetzt wie erwartet arbeiten
jkebinger