How do you use script variables in psql?
Postgres variables are created through the \set command, for example ...
\set myvariable value
... and can then be substituted, for example, as ...
SELECT * FROM :myvariable.table1;
... or ...
SELECT * FROM table1 WHERE :myvariable IS NULL;
edit: As of psql 9.1, variables can be expanded in quotes as in:
\set myvariable value
SELECT * FROM table1 WHERE column1 = :'myvariable';
In older versions of the psql client:
... If you want to use the variable as the value in a conditional string query, such as ...
SELECT * FROM table1 WHERE column1 = ':myvariable';
... then you need to include the quotes in the variable itself as the above will not work. Instead define your variable as such ...
\set myvariable 'value'
However, if, like me, you ran into a situation in which you wanted to make a string from an existing variable, I found the trick to be this ...
\set quoted_myvariable '\'' :myvariable '\''
Now you have both a quoted and unquoted variable of the same string! And you can do something like this ....
INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
How to declare a variable in postgres script?
You are confused on several levels.
There is the query language SQL, and there is the procedural language PL/pgSQL. The only connection is that
you can run SQL statements from PL/pgSQL code
you can have PL/pgSQL code in the body of the SQL statements
DO
andCREATE FUNCTION/PROCEDURE
.
There are variables in PL/pgSQL, which are defined in the
DECLARE
section, but there are no variables in SQL.DO
statements cannot return any values.
If you want to use PL/pgSQL variables, and you want to return values, you'll have to use a function. An example:
CREATE FUNCTION getpersons() RETURNS SETOF person
LANGUAGE plpgsql AS
$$DECLARE
overTheAgeOf int := 15;
BEGIN
RETURN QUERY
SELECT *
FROM person
WHERE age > overTheAgeOf;
END;$$;
SELECT getpersons();
There is the alternative of using variables on the client. With the psql
client, you could use:
\set overTheAgeOf 15
SELECT *
FROM person
WHERE age > :overTheAgeOf;
How do I use a variable in Postgres scripts?
I can think of three approaches, since psql
cannot do this directly.
Shell script
Use a bash script to perform the variable substitution and pipe the results into psql
, like.
#!/bin/bash
$schemaName = $1
$contents = `cat script.sql | sed -e 's/@SCHEMA_NAME@/$schemaName'`
echo $contents | psql
This would probably be a lot of boiler plate if you have a lot of .sql scripts.
Staging Schema
Keep the approach you have now with a hard-coded schema of something like staging
and then have a bash script go and rename staging
to whatever you want the actual schema to be.
Customize the search path
Your entry point could be an inline script within bash that is piped into psql, does an up-front update of the default connection schema, then uses \ir to include all of your .sql
files, which should not specify a schema.
#!/bin/bash
$schemaName = $1
psql <<SCRIPT
SET search_path TO $schemaName;
\ir sql/file1.pgsql
\ir sql/file2.pgsql
SCRIPT
Some details: How to select a schema in postgres when using psql?
Personally I am leaning towards the latter approach as it seems the simplest and most scalable.
how to use variables in a psql script
First of all, you are trying to concatenate two strings with +
operator, but the SQL operator for concatenation is ||
, with that information, you could think the expected result would be (won't work):
\set path '''c:\\server\\data\\'''
COPY paymentMethods (name,regexString) FROM :path || 'paymentMethods.csv' WITH (FORMAT csv, HEADER true);
COPY priceLevels (name) FROM :path || 'priceLevels.csv' WITH (FORMAT csv, HEADER false);
But! The COPY
command expect a literal string for the path, not an expression, so you really should give the path. Notice it would work for commands like SELECT
, INSERT
, UPDATE
, etc.
With that information, you can only use psql
variables, as Pavel pointed, and concatenate the strings into a psql
's variable. A good solution is to use the psql
's :'var'
syntax, that insert the variable as a string into the SQL expression:
\set path 'c:\\server\\data\\'
\set paymentMethodsPath :path 'paymentMethods.csv'
\set priceLevelsPath :path 'priceLevels.csv'
COPY paymentMethods (name,regexString) FROM :'paymentMethodsPath' WITH (FORMAT csv, HEADER true);
COPY priceLevels (name) FROM :'priceLevels' WITH (FORMAT csv, HEADER false);
Which will generate (will send to the PostgreSQL's server):
COPY paymentMethods (name,regexString) FROM E'c:\\server\\data\\paymentMethods.csv' WITH (FORMAT csv, HEADER true);
COPY priceLevels (name) FROM E'c:\\server\\data\\priceLevels.csv' WITH (FORMAT csv, HEADER false);
This :'var'
syntax will not work on all psql
versions (I don't recall now which one this was introduced), but for old versions you can easily use dollar-quoting:
\set path 'c:\\server\\data\\'
\set paymentMethodsPath :path 'paymentMethods.csv'
\set priceLevelsPath :path 'priceLevels.csv'
COPY paymentMethods (name,regexString) FROM $$:paymentMethodsPath$$ WITH (FORMAT csv, HEADER true);
COPY priceLevels (name) FROM $$:priceLevels$$ WITH (FORMAT csv, HEADER false);
Or escape into single-quotes:
\set path 'c:\\server\\data\\'
\set paymentMethodsPath 'E''':path'paymentMethods.csv'''
\set priceLevelsPath 'E''':path'priceLevels.csv'''
COPY paymentMethods (name,regexString) FROM :paymentMethodsPath WITH (FORMAT csv, HEADER true);
COPY priceLevels (name) FROM :priceLevels WITH (FORMAT csv, HEADER false);
That is it.
PSQL passing external variables
A DO
statement executes an anonymous code block. The "$do$ tags" are dollar-quotes. Read this first to understand:
- What are '$$' used for in PL/pgSQL
BEGIN
and END
are decorators for a PL/pgSQL code block - which is the default PL (programming language) of a DO
statement.
psql does not interpolate variables inside quoted SQL literals and identifiers.
One way to fix your problem is using Postgres string processing and then execute the result with \gexec
. From psql:
SELECT format($$
DO
$do$
BEGIN
IF %L = 'test' THEN
-- do something
END IF;
END
$do$;
$$, :'env')\gexec
There are various other ways. Maybe a (temporary) function can serve you better: you can pass parameters to it. Depends on the complete picture.
See this answers to a very similar question on dba.SE:
- How to pass variable to PL/pgSQL code from the command line?
Related Topics
Xcode 4 and Core Data: How to Enable SQL Debugging
How to Find the MySQL My.Cnf Location
MySQL - Error 1045 - Access Denied
Bash Script to Insert Values in MySQL
Commit Data in a MySQL Container
Pass R Variable to Rodbc'S Sqlquery
Ruby Gem MySQL2 Install Failing
Rails 4 Like Query - Activerecord Adds Quotes
Correct MySQL Configuration For Ruby on Rails Database.Yml File
Accessing an Sqlite Database in Swift
Get Top N Records For Each Group of Grouped Results
How Important Is the Order of Columns in Indexes