Update a PostgreSQL field from the content of a file - postgresql

I have a file containing a value which should go into a field of a PostgreSQL table.
By searching a little, I found many answers, e.g. How can I update column values with the content of a file without interpreting it? or https://stackoverflow.com/a/14123513/6630397, with this kind of snippet, but it has to be run in a psql terminal:
\set content `cat /home/username/file.txt`
UPDATE table SET field = :'content' WHERE id=1;
It works, but is it possible to programmatically execute it in one shot, directly from a bash prompt, without manually entering the psql command line, e.g. something like:
$ psql -d postgres://postgres#localhost/mydatabase -c \
"UPDATE table SET field = :'the_file_content' WHERE id=1;"
?
There is also the -v argument that seems promising but I'm not successful when using it:
$ psql -d postgres://postgres#localhost/mydatabase \
-v content=`cat ${HOME}/file.txt` \
-c "UPDATE table SET field = :'content' WHERE id=1;"
I've got thousands of psql: warning: extra command-line argument where psql actually seems to "execute" each comma separated strings of the file as pg commands, where it shouldn't of course; the file content, which consists of a single line, must be treated as a whole.
Doc PostgreSQL 14:
https://www.postgresql.org/docs/current/app-psql.html

How about reading the file content into a variable first and then use it?
content=$(<integer_infile); psql -p 5434 -c "update table set field = $content where id = 1;"
content=$(<text_infile); psql -p 5434 -c "update table set field = '$content' where id = 1;"
This at least works for me if the file contains an integer or text including spaces on a single line.

Related

PostgreSQL / psql meta-command silently fails and doesn't insert rows

I've created a SQL file I run through the psql command that roughly looks like as follows:
truncate table my_table;
\set content `cat /workdir/test.json` insert into my_table values ('test_row', :'content');
The first line is somewhat irrelevant to the problem, except for the fact it does print out "TRUNCATE TABLE", so it is reading and running the SQL file correctly, at least initially. However, the insert row is never created, the table is always empty. Yet no error message pops up.
The JSON file has a valid value (even if I pare it down to super basic {}). I've also tried passing the sql command directly (just to cover my bases, tried it with just one '' and same, with three it gives invalid command error):
psql [...] -c "\\set content `cat /workdir/test.json` insert into my_table values ('test_row', :'content')"
Again, no output message, no new rows created. However not using the meta-command \set does work. E.g.:
psql [...] -c "insert into my_table values ('test_row', '{}')"
Seems like there's something it doesn't like about the meta-command \set, but without any error info, not sure what I'm doing wrong.
Both the script and database are running on the same VM. That is, script can call host via 'localhost' and the filesystem/filepaths should be the same, I think, should that matter.
A psql meta-command (something that starts with a backslash) are terminated by the end of line; you cannot have an SQL statement on the same line.
Write the \set in one line and the INSERT in another.
If you want to use the -c option of psql, use several -c options:
psql -c "\\set ..." -c "INSERT ..."

PostgreSQL copy command gives error for temporary table

I am trying to run the below command on the unix console :
env 'PGOPTIONS=-c search_path=admin -c client_min_messages=error' psql -h hostname -U user -p 1111 -d platform -c "CREATE TEMP TABLE admin.tmp_213 AS SELECT * FROM admin.file_status limit 0;\copy admin.tmp_213(feed_id,run_id,extracted_date,file_name,start_time,file_size,status,crypt_flag) FROM '/opt/temp/213/list.up' delimiter ',' csv;UPDATE admin.file_status SET file_size = admin.tmp_213.file_size FROM admin.tmp_213 A WHERE admin.file_status.feed_id = A.feed_id and admin.file_status.file_name = A.file_name;"
I am getting the below error:
ERROR: syntax error at or near "\"
LINE 1: ...* FROM admin.file_status limit 0;\copy admi...
If I use the above command without a backslash before COPY, it gives me the below error:
ERROR: cannot create temporary relation in non-temporary schema
I am doing the above to implement the solution as mentioned here:
How to update selected rows with values from a CSV file in Postgres?
Your first error is because metacommands like \copy cannot be combined in the same line as regular commands when given with -c. You can give two -c options, with one command in each instead.
The second error is self-explanatory. You don't get to decide what schema your temp table goes to. Just omit the schema.

Set array using psql interface

I have a function that adds users to the database from an array, e.g.
psql -c "SELECT addUsersFromList(array['userA','userB','userC'])"
This works fine, however I would prefer to be able to run this as a script, like so:
psql -f add_users.sql -v userlist=array['userA','userB','userC'])
And add_user.sql:
SELECT addUsersFromList(:userlist);
When I execute the above psql command, I get an error:
psql:Scripts/add_users.sql:39: ERROR: column "userA" does not exist
This seems to be an issue with how I use the -v flag. I had a look at the postgres documentaion on -v, \set and the Variables section on that same page, but could not find a way to assign an array.
Try to write the array as a string literal:
psql -f add_users.sql -v "userlist='{userA,userB,userC}'"

How to split PSQL command lines over multiple lines?

I'm using a windows batch file to connect to postgres using psql. I'm issuing commands like this....
SET PGPASSWORD=postgres
psql -U postgres -d postgres -c "DROP USER IF EXISTS foo;"
This works fine for running one, short SQL command against the database. But I'm having trouble with two related issues
How to continue a single long SQL command over multiple lines, and
How to run multiple commands.
Example 1.....
psql -U postgres -d postgres -c "CREATE DATABASE foo
WITH OWNER = bar
ENCODING = 'UTF8'
TABLESPACE = mytabspace;"
Example 2.....
psql -U postgres -d postgres -c "
ALTER TABLE one ALTER COLUMN X TYPE INTEGER;
ALTER TABLE two ALTER COLUMN Y TYPE INTEGER;"
Neither of these will work as shown, I've done some googling and found some suggestions for doing this with linux, and have experimented with various carats, backslashes and underscores, but just don't seem to be able to split the commands across lines.
I'm aware of the -f option to run a file, but I'm trying to avoid that.
Any suggestions?
The line continuation character in batch is the ^. See this Q&A
So end the line with space+caret ^ and make sure the following line begins with a space.
You will also have to escape double quoted areas that span several lines with a caret for this to work.
Since the line is unquoted then for the batch parser you will also have to escape any special chararacters like <>|& also with a caret.
psql -U postgres -d postgres -c ^"CREATE DATABASE foo ^
WITH OWNER = bar ^
ENCODING = 'UTF8' ^
TABLESPACE = mytabspace;"
psql -U postgres -d postgres -c ^" ^
ALTER TABLE one ALTER COLUMN X TYPE INTEGER; ^
ALTER TABLE two ALTER COLUMN Y TYPE INTEGER;"

How can I specify the schema to run an sql file against in the Postgresql command line

I run scripts against my database like this...
psql -d myDataBase -a -f myInsertFile.sql
The only problem is I want to be able to specify in this command what schema to run the script against. I could call set search_path='my_schema_01' but the files are supposed to be portable. How can I do this?
You can create one file that contains the set schema ... statement and then include the actual file you want to run:
Create a file run_insert.sql:
set schema 'my_schema_01';
\i myInsertFile.sql
Then call this using:
psql -d myDataBase -a -f run_insert.sql
More universal way is to set search_path (should work in PostgreSQL 7.x and above):
SET search_path TO myschema;
Note that set schema myschema is an alias to above command that is not available in 8.x.
See also: http://www.postgresql.org/docs/9.3/static/ddl-schemas.html
Main Example
The example below will run myfile.sql on database mydatabase using schema myschema.
psql "dbname=mydatabase options=--search_path=myschema" -a -f myfile.sql
The way this works is the first argument to the psql command is the dbname argument. The docs mention a connection string can be provided.
If this parameter contains an = sign or starts with a valid URI prefix
(postgresql:// or postgres://), it is treated as a conninfo string
The dbname keyword specifies the database to connect to and the options keyword lets you specify command-line options to send to the server at connection startup. Those options are detailed in the server configuration chapter. The option we are using to select the schema is search_path.
Another Example
The example below will connect to host myhost on database mydatabase using schema myschema. The = special character must be url escaped with the escape sequence %3D.
psql postgres://myuser#myhost?options=--search_path%3Dmyschema
The PGOPTIONS environment variable may be used to achieve this in a flexible way.
In an Unix shell:
PGOPTIONS="--search_path=my_schema_01" psql -d myDataBase -a -f myInsertFile.sql
If there are several invocations in the script or sub-shells that need the same options, it's simpler to set PGOPTIONS only once and export it.
PGOPTIONS="--search_path=my_schema_01"
export PGOPTIONS
psql -d somebase
psql -d someotherbase
...
or invoke the top-level shell script with PGOPTIONS set from the outside
PGOPTIONS="--search_path=my_schema_01" ./my-upgrade-script.sh
In Windows CMD environment, set PGOPTIONS=value should work the same.
I'm using something like this and works very well:* :-)
(echo "set schema 'acme';" ; \
cat ~/git/soluvas-framework/schedule/src/main/resources/org/soluvas/schedule/tables_postgres.sql) \
| psql -Upostgres -hlocalhost quikdo_app_dev
Note: Linux/Mac/Bash only, though probably there's a way to do that in Windows/PowerShell too.
This works for me:
psql postgresql://myuser:password#myhost/my_db -f myInsertFile.sql
In my case, I wanted to add schema to a file dynamically so that whatever schema name user will provide from the cli, I will run sql file with that provided schema name.
For this, I replaced some text in the sql file. First I added {{schema}} in the file like this
CREATE OR REPLACE FUNCTION {{schema}}.usp_dailygaintablereportdata(
then replace {{schema}} dynamically with user provided schema name with the help of sed command
sed -i "s/{{schema}}/$pgSchemaName/" $filename
result=$(psql -U $user -h $host -p $port -d $dbName -f "$filename" 2>&1)
sed -i "s/$pgSchemaName/{{schema}}/" $filename
First replace is done, then target file is run and then again our replace is reverted back
I was facing similar problems trying to do some dat import on an intermediate schema (that later we move on to the final one). As we rely on things like extensions (for example PostGIS), the "run_insert" sql file did not fully solved the problem.
After a while, we've found that at least with Postgres 9.3 the solution is far easier... just create your SQL script always specifying the schema when refering to the table:
CREATE TABLE "my_schema"."my_table" (...);
COPY "my_schema"."my_table" (...) FROM stdin;
This way using psql -f xxxxx works perfectly, and you don't need to change search_paths nor use intermediate files (and won't hit extension schema problems).