Psycopg2 issue with double-dollar $$ sign - python

I have an issue with $$ in psycopg2 library. For testing purposes I've created simple code snippet like
CREATE OR REPLACE PROCEDURE test()
AS $$
BEGIN
select 1;
END
$$ LANGUAGE plpgsql;
and if I process it in any postgresql client it's all fine, but processing in psycopg causes unterminated dollar-quoted string at or near "$$...
Any idea how to fix it?

It works for me doing:
import psycopg2
con = psycopg2.connect(dbname="test", host='localhost', user='postgres')
proc_sql = """CREATE OR REPLACE PROCEDURE test()
AS $$
BEGIN
select 1;
END
$$ LANGUAGE plpgsql;"""
cur = con.cursor()
cur.execute(proc_sql)
con.commit()

Related

How to run Oracle PL/SQL in python

I'm using Jupyter notebook to run a PL/SQL script but I get an error. The code block in the notebook is as follows:
%%sql
DECLARE BEGIN
FOR record_item IN (
SELECT
*
FROM
duplicated_records
) LOOP
EXECUTE IMMEDIATE 'UPDATE table_name SET record_id ='|| record_item.original_record_id || ' WHERE record_id =' || record_item.duplicated_record_id;
EXECUTE IMMEDIATE 'DELETE FROM records WHERE id ='|| record_item.duplicated_record_id;
END LOOP;
END
The error is
(cx_Oracle.DatabaseError) ORA-06550: line 8, column 165:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
Non-PL/SQL code, such as select, and update statements, seems to work.
It works perfectly fine with other SQL clients like SQL developer. I've tried adding/removing the; at the end but it still doesn't work.
I don't know Python so I can't assist about that, but - as far as Oracle is concerned - you don't need DECLARE (as you didn't declare anything), and you certainly don't need dynamic SQL (EXECUTE IMMEDIATE) as there's nothing dynamic there.
Rewritten:
BEGIN
FOR record_item IN (SELECT * FROM duplicated_records) LOOP
UPDATE table_name
SET record_id = record_item.original_record_id
WHERE record_id = record_item.duplicated_record_id;
DELETE FROM records
WHERE id = record_item.duplicated_record_id;
END LOOP;
END;
On the other hand, row-by-row processing is slow-by-slow. Consider using two separate statements: one which will update existing rows, and another which will delete rows (from a different table, apparently):
merge into table_name a
using duplicated_records b
on (a.record_id = b.duplicate_record_id)
when matched then update set
a.record_id = b.original_record_id;
delete from records a
where a.id in (select b.duplicated_record_id from duplicated_records b);
If tables are properly indexed (on ID columns), that should behave better (faster).
The direct implementation of your code in Python would be like:
import oracledb
import traceback
import os
import sys
#if sys.platform.startswith('darwin'):
# oracledb.init_oracle_client(lib_dir=os.environ.get('HOME')+'/Downloads/instantclient_19_8')
un = os.environ.get('PYTHON_USERNAME')
pw = os.environ.get('PYTHON_PASSWORD')
cs = os.environ.get('PYTHON_CONNECTSTRING')
try:
connection = oracledb.connect(user=un, password=pw, dsn=cs)
with connection.cursor() as cursor:
plsql = """BEGIN
FOR RECORD_ITEM IN (
SELECT
*
FROM
DUPLICATED_RECORDS
) LOOP
EXECUTE IMMEDIATE 'UPDATE table_name SET record_id ='
|| RECORD_ITEM.ORIGINAL_RECORD_ID
|| ' WHERE record_id ='
|| RECORD_ITEM.DUPLICATED_RECORD_ID;
EXECUTE IMMEDIATE 'DELETE FROM records WHERE id ='
|| RECORD_ITEM.DUPLICATED_RECORD_ID;
END LOOP;
END;"""
cursor.execute(plsql)
except oracledb.Error as e:
error, = e.args
traceback.print_tb(e.__traceback__)
print(error.message)
For this you need to install the oracledb module, which is the renamed, latest version of the cx_Oracle module. It will work with cx_Oracle by changing the import to import cx_Oracle as oracledb.
However, before blindly copying this, check #littlefoot's answer for more about the PL/SQL code.

Python2 print postgresql stored procedure raise notice

How can I print postgres's stored procedure on python script?
Example of stored procedure in postgres is as below:
create or replace function checktime() returns void
language plpgsql
as $$
DECLARE timestart TIMESTAMP;
FOR id from rt LOOP
SELECT timeofday() into timestart;
RAISE NOTICE 'Time now : %', timestart;
END LOOP;
END;
$$
;
From python, my script is:
import psycopg2
conn = psycopg2.connect(host="", database="",
user="", password="")
print("Database Connected")
cur = conn.cursor()
rowcount = cur.rowcount
cur.callproc('rt_visits_function_gz')
# how can i display the raise notice in here?
I would like for each loop the result is displayed when i run python.
Thank you
Try using 'notices'
print(conn.notices)
http://initd.org/psycopg/docs/connection.html?highlight=notice#connection.notices

Python2.7 - SQLite3 library outputs error message "sqlite3.OperationalError: near "?": syntax error"

Code is follow. How to get replaced ? by value of variables [table, url]?
Expected SQL command is select * from OTHER_URL where url="http://a.com/a.jpg"
This SQL command occurs no error on the sqlite3 command line interface.
import sqlite3
from contextlib import closing
dbname = "ng.db"
with closing(sqlite3.connect(dbname)) as conn:
c = conn.cursor()
c.execute("CREATE TABLE IF NOT EXISTS OTHER_URL (url TEXT)")
conn.commit()
table = "OTHER_URL"
url = "http://a.com/a.jpg"
with closing(sqlite3.connect(dbname)) as conn:
c = conn.cursor()
c.execute('select * from ? where url="?"', [table, url])
print c.fetchone()
There are two errors here. Firstly, you can't use parameter substitution for table names (or column names), only for values. You need to use string interpolation for anything else.
Secondly, you don't need quotes around the value parameter; the substitution will take care of that.
So:
c.execute('select * from {} where url=?'.format(table), [url])

Psycopg2 : Create a table in a stored procedure Postgres

Stored Procedure :
CREATE OR REPLACE FUNCTION try_create() RETURNS INT AS $$
BEGIN
CREATE TABLE hello(id SERIAL PRIMARY KEY, name TEXT);
RETURN 1;
END ;
$$ LANGUAGE plpgsql;
test.py
import psycopg2
conn = psycopg2.connect(user='a', password='a', dbname='a')
cur = conn.cursor()
cur.callproc('try_create', ())
print cur.fetchall()
I am trying to create a stored procedure which will create a table named hello. I am invoking the same using a python script. Upon running the above script I see the following output
[root#localhost partitioning]# python test.py
[(1,)]
But the table is not created at the db. Am I making something wrong here? Thanks.
You should commit the transaction, add the commands:
...
conn.commit()
conn.close()
Alternatively, you can set the connection in autocommit mode:
conn = psycopg2.connect(user='a', password='a', dbname='a')
conn.autocommit = True
cur = conn.cursor()
cur.callproc('try_create', ())
conn.close()
Read more about transactions in psycopg2.

How I can make same results on cx_Oracle and Sql * Plus?

It's - for sqlplus - commands:
SQL> set serveroutput on
SQL> exec where.my_package.ger_result('something');
something=1823655138
And it's - for cx_Oracle:
>>> c.callproc('where.my_package.ger_result', ('something',))
['something']
As You can see - the results are different.
I have no idea, how to fix it. :[
import cx_Oracle
dsn_tns = cx_Oracle.makedsn('my_ip_address_server_next_port', 0000, 'sid')
db = cx_Oracle.connect('user', 'password', dsn_tns)
curs = db.cursor()
curs.callproc("dbms_output.enable")
curs.callproc('where.my_package.ger_result', ['something',])
statusVar = curs.var(cx_Oracle.NUMBER)
lineVar = curs.var(cx_Oracle.STRING)
while True:
curs.callproc("dbms_output.get_line", (lineVar, statusVar))
if statusVar.getvalue() != 0:
break
print lineVar.getvalue()
Sorry, I can't reproduce this one.
I don't have your PL/SQL package, so I used the following stored procedure instead:
CREATE OR REPLACE PROCEDURE p_do_somet (
p_param IN VARCHAR2
) AS
BEGIN
dbms_output.put_line(p_param || '=1823655138');
END;
/
I got the same output, something=1823655138, from SQL*Plus and from using the Python script in your answer.
If you're getting different results using SQL*Plus and cx_Oracle, then either your stored procedure is doing something very funny (I don't know what could cause it to do this), or your SQL*Plus session and Python script are not connecting to the same database and/or schema.

Categories