PostgreSQL, plpython3u function. os.getenv does not find the envinronment variable - postgresql

I get the below situation with PostgreSQL plpython3u function.
I've recently added a envinronment variable to my shell startup file. The env | grep MY_VAR command returns MY_VAR=/path/. But when I run a select * from my_func(); query from a psql command line, the function says that the envinronment variable is None.
create or replace function my_func()
returns int as
$$
import os
for k, v in os.environ.items():
plpy.info(k + ':' + v)
MY_VAR = os.getenv('MY_VAR')
if MY_VAR is None:
plpy.info('was not found')
return -1
plpy.info('MY_VAR:' + MY_VAR)
return 0
$$ language 'plpython3u';
returns int AS
The for k, v in os.environ.items(): ... prints all variables except the one I search for. The MY_VAR gets missed somehow.
Please advice. Why did the function can't find MY_VAR?
Update
The \! env command in psql command prompt also lists MY_VAR

As it turned out, the PostgreSQL server only reads envinronment variables once, upon the main server process start. I had to restart the server to get access to full envinronment varaibles list.

Related

psql \i metacommand with arguments

I have a sql script which i run for by-hand status checks of a long running process. My server is currently postgres 10. The script contains a couple dozen queries. However, each query requires the id of the current process (not linux pid, but a column in the db).
So, it's like
SELECT * FROM table1 WHERE id = 'xxx';
SELECT * FROM table2 WHERE id = 'xxx';
... and so forth
I have a sql script that i keep updating with the most recent id, and run with
db# \i my-script.sql
It would be great if I could pass variables into the script. For example, maybe the script is
SELECT * FROM table1 WHERE id = '$1';
SELECT * FROM table2 WHERE id = '$1';
and i could run
db# \i my-script.sql 'xxx'
and it would properly substitute that value for all $1 in the sql script. Bonus points for multiple args (eg $2 and so on).
OTOH, I could write a bash script wrapping psql command line, but I'd need to drop to a shell to execute it - if i'm already in psql, I'm right there.
Any advice?
Thanks!
Use psql variables.
In the script, write
\prompt 'Please enter id:' myid
SELECT * FROM table1 WHERE id = :'myid';
If you don't want an interactive prompt, omit the \prompt and set the variable in the calling script:
\set myid 1234

How to write a PL/SQL script that can run as Script in Oracle's SQLPlus as well as in SQLDeveloper?

I have developed a script using SQLDeveloper.
It's basic structure is:
File pl_sql.sql:
-- setting some options here, e.g. set linesize 32767 / set serveroutput on / etc.
declare
-- some variables declared here
begin
-- some SQL statements and PL/SQL code here
end;
This runs fine in SQLDeveloper.
To run this in SQLPlus I had to write a wrapper like so:
File wrapper.sql:
#pl_sql.sql
/
quit
Without that '/' the script isn't executed but I get a prompt only. However, when I call this I always get an error at the first variable declaration after the declare.
As I found out - using a lot of trial and error - I apparently can't have the options preceeding the "declare" in the called pl_sql.sql file. So I moved the options to the wrapper.sql like so:
File wrapper2.sql:
-- setting some options here, e.g. set linesize 32767 / set serveroutput on / etc.
#pl_sql2.sql
/
quit
and the script is exactly the same as the first but without the leading options:
File pl_sql2.sql:
declare
-- some variables declared here
begin
-- some SQL statements and PL/SQL code here
end;
But that variant of the PL/SQL script now of course doesn't work in SQLDeveloper any more. Or more precise: it works but doesn't generate any output (because the "set serveroutput on" and other options are missing now).
Is it really not possible to include these options somehow into the inner file and have the wrapper really be just be:
File wrapper.sql:
#pl_sql2.sql
/
quit
i.e. just the call of the file and the trailing '/' + quit?
Or even better: could one not call the inner .sql file WITH the options AND the code from slqplus directly and have it execute that script without requiring that stupid wrapper just to append that '/'?
Hope I could make myself clear...
From what i understand, you have an sql file that you would like to run this in SQLPlus. While running the block in SqlPlus, it always needs the forward slash /, where by you are telling Sqlplus, i've finished my proc definition, now run it (As in the proc each line is terminated by a semi-colon, sqlplus has no way to identify when it should execute the statement ). Its perfrectly fine to have the set commands in ur plsql.sql file. I suspect you are getting errors because of any non printable characters in the sql file.
Below is a copy of my script, and it works fine.
$cat slqtest.sh
#!/bin/sh
sqlplus -s ${DBUSER}/${DBUSERPASS}#//${DBHOST}:${DBPORT}/${DBSERVICENAME}<<EOSQL
#sqltest.sql
/
EOSQL
$ cat sqltest.sql
set linesize 32767
set serveroutput on
declare
l_string varchar2(100);
begin
select 'This works' into l_string from dual;
dbms_output.put_line('l_string '||l_string);
end;
$ ./slqtest.sh
l_string This works
PL/SQL procedure successfully completed.

psql function written in separate file

i'm learning psql in Postgres.
My basic question is when i create a function like this one :
CREATE OR REPLACE FUNCTION totalRecords ()
RETURNS integer AS $total$
declare
total integer;
BEGIN
SELECT count(*) into total FROM COMPANY;
RETURN total;
END;
$total$ LANGUAGE plpgsql;
i must write all of the code in the prompt command line.
How can i save this code in a script and call it from the command line ? the extentsion of the scirpt must be a .sql ? how can i call this script.
Save your script to a file. Then execute like this:
psql -p portnumber -d database -U user -f mysqlscrpt.sql
The extension of the script does not matter.
#jkdba's answer is sufficient but, alternatively, if you are already logged in the psql console, you can use:
\i <sqlscript>.sql
The \i being for input. \o is output, and it would write to the file instead of bringing it in.
I mention this because this is what made me remember the command :)

How invoke PL/pgSQL from command line instead of SQL

I write PL/SQL scripts using Oracle and I'm trying my first PL/pgSQL script with PostgreSQL. For example, I created this test.sql file.
DECLARE
v_loc_nbr INTEGER;
BEGIN
v_loc_nbr := 0;
END;
Which I try to execute using the command line:
\postgresql\9.3\bin\psql.exe -d postgres -f test.sql
but I get syntax errors like:
psql:test.sql:4: ERROR: syntax error at or near "v_loc_nbr"
I think the problem is it trying to execute SQL when I want it to run PL/pgSQL. What should the command be?
Like with other procedural languages, you cannot run PL/pgSQL directly.
You can only run SQL. Use plpgsql inside a function body or wrapped in a DO command, where the range of DO commands is limited since they cannot return data.
Check out the plpgsql tag for examples.
I don't want to explain more about this because Erwin explained well.You need to wrap your sql inside a DO, so your test.sql should write like this
DO
$$
DECLARE
v_loc_nbr INTEGER;
BEGIN
v_loc_nbr := 0;
END;
$$
and try to execute it \postgresql\9.3\bin\psql.exe -d postgres -f test.sql

PostgreSQL - batch + script + variable

I am not a programmer, I am struggling a bit with this.
I have a batch file connecting to my PostgreSQL server, and then open a sql script. Everything works as expected. My question is how to pass a variable (if possible) from one to the other.
Here is my batch file:
set PGPASSWORD=xxxx
cls
#echo off
C:\Progra~1\PostgreSQL\8.3\bin\psql -d Total -h localhost -p 5432 -U postgres -f C:\TotalProteinImport.sql
And here's the script:
copy totalprotein from 'c:/TP.csv' DELIMITERS ',' CSV HEADER;
update anagrafica
set pt=(select totalprotein.resultvalue from totalprotein where totalprotein.accessionnbr=anagrafica.id)
where data_analisi = '12/23/2011';
delete from totalprotein;
This is working great, now the question is how could I pass a variable that would carry the date for data_analisi?
Like in the batch file, "Please enter date", and then the value is passed to the sql script.
You could create a function out of your your SQL script like this:
CREATE OR REPLACE FUNCTION f_myfunc(date)
RETURNS void AS
$BODY$
CREATE TEMP TABLE t_tmp ON COMMIT DROP AS
SELECT * FROM totalprotein LIMIT 0; -- copy table-structure from table
COPY t_tmp FROM 'c:/TP.csv' DELIMITERS ',' CSV HEADER;
UPDATE anagrafica a
SET pt = t.resultvalue
FROM t_tmp t
WHERE a.data_analisi = $1
AND t.accessionnbr = a.id;
-- Temp table is dropped automatically at end of session
-- In this case (ON COMMIT DROP) after the transaction
$BODY$
LANGUAGE sql;
You can use language SQL for this kind of simple SQL batch.
As you can see I have made a couple of modifications to your script that should make it faster, cleaner and safer.
Major points
For reading data into an empty table temporarily, use a temporary table. Saves a lot of disc writes and is much faster.
To simplify the process I use your existing table totalprotein as template for the creation of the (empty) temp table.
If you want to delete all rows of a table use TRUNCATE instead of DELETE FROM. Much faster. In this particular case, you need neither. The temporary table is dropped automatically. See comments in function.
The way you updated anagrafica.pt you would set the column to NULL, if anything goes wrong in the process (date not found, wrong date, id not found ...). The way I rewrote the UPDATE, it only happens if matching data are found. I assume that is what you actually want.
Then ask for user input in your shell script and call the function with the date as parameter. That's how it could work in a Linux shell (as user postgres, with password-less access (using IDENT method in pg_haba.conf):
#! /bin/sh
# Ask for date. 'YYYY-MM-DD' = ISO date-format, valid with any postgres locale.
echo -n "Enter date in the form YYYY-MM-DD and press [ENTER]: "
read date
# check validity of $date ...
psql db -p5432 -c "SELECT f_myfunc('$date')"
-c makes psql execute a singe SQL command and then exits. I wrote a lot more on psql and its command line options yesterday in a somewhat related answer.
The creation of the according Windows batch file remains as exercise for you.
Call under Windows
The error message tells you:
Function tpimport(unknown) does not exist
Note the lower case letters: tpimport. I suspect you used mixe case letters to create the function. So now you have to enclose the function name in double quotes every time you use it.
Try this one (edited quotes!):
C:\Progra~1\PostgreSQL\8.3\bin\psql -d Total -h localhost -p 5432 -U postgres
-c "SELECT ""TPImport""('%dateimport%')"
Note how I use singe and double quotes here. I guess this could work under windows. See here.
You made it hard for yourself when you chose to use mixed case identifiers in PostgreSQL - a folly which I never tire of warning against. Now you have to double quote the function name "TPImport" every time you use it. While perfectly legit, I would never do that. I use lower case letters for identifiers. Always. This way I never mix up lower / upper case and I never have to use double quotes.
The ultimate fix would be to recreate the function with a lower case name (just leave away the double quotes and it will be folded to lower case automatically). Then the function name will just work without any quoting.
Read the basics about identifiers here.
Also, consider upgrading to a more recent version of PostgreSQL 8.3 is a bit rusty by now.
psql supports textual replacement variables. Within psql they can be set using \set and used using :varname.
\set xyz 'abcdef'
select :'xyz';
?column?
----------
abcdef
These variables can be set using command line arguments also:
psql -v xyz=value
The only problem is that these textual replacements always need some fiddling with quoting as shown by the first \set and select.
After creating the function in Postgres, you must create a .bat file in the bin directory of your Postgres version, for example C:\Program Files\PostgreSQL\9.3\bin. Here you write:
#echo off
cd C:\Program Files\PostgreSQL\9.3\bin
psql -p 5432 -h localhost -d myDataBase -U postgres -c "select * from myFunction()"