I have an oracle procedure that is supposed to return a chunk of json.
The procedure has 4 paramaters
input is an ID value
output json CLOB
output some message in json format CLOB
output if success or failure varchar2
so in my code I have done the following just to test if I can successfully call and return it
ConnectionString = 'someconnection.connection'
con = cx_Oracle.connect(ConnectionString)
cur = con.cursor()
ID = '51858645'
json_out = cur.var(cx_Oracle.CLOB)
message = cur.var(cx_Oracle.CLOB)
status = cur.var(cx_Oracle.STRING)
oracle_return = cur.callproc('project.getsomejson',[ID,json_out,message,status])
However, it fails and returns
PLS-00306: wrong number or types of arguments in call to 'getsomejson'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
This is the procedure definition
procedure callOracle(json_in clob,json_out out clob,message out clob,status out varchar2)
This is calling an oracle 12c db
I'm really not familiar at all with calling procedures in python. Typically I callfunc with the stored type and just get a return
The procedure is expecting
CLOB, CLOB, CLOB, VARCHAR2
but you are passing
VARCHAR2, CLOB, CLOB, VARCHAR2
The name you gave (callOracle) also doesn't match what you are calling in Python (project.getsomejson). Perhaps verify that you have the right procedure signature? Assuming it is correct, though, you'll need to change the first one to be a CLOB as well or change the stored procedure to accept VARCHAR2. Something like this should do it:
json_in = conn.createlob(cx_Oracle.DB_TYPE_CLOB)
json_in.write("51858645")
json_out_var = cur.var(cx_Oracle.DB_TYPE_CLOB)
message_var = cur.var(cx_Oracle.DB_TYPE_CLOB)
status_var = cur.var(str)
cur.callproc(json_in, json_out, message, status)
Related
Imagine this is my query :
query = '''
SELECT *
FROM table
WHERE id = {{myid}}'''
params = {'myid':3}
j= JinJaSql(param_style='pyformat'}
myquery, bind_params = j.prepare_query(query,params)
when I print bind_params I would get
{'myid_1':3}
why my parameter name was changed to myid_1 while I named it myid. Is there anything wrong with my code? How can I fix it?
According to the readme on the JinjaSql github page, if using the "pyformat" or "named" param style, the bound parameters returned by prepare_query are guaranteed to be unique. I suspect that this is why myid gets changed to myid_1.
When calling read_sql you should make sure to use the myquery and bind_params returned by prepare_query and not the query and params used in the prepare_query call, i.e.
# ...
# your code above
# ...
myquery, bind_params = j.prepare_query(query, params)
result = pd.read_sql(myquery, conn, params=bind_params)
Note that the format of your params for pd.read_sql depends on the SQL dialect you're using (docs). If you're using postgres for example, dictionary params are the way to go.
Hope this helps!
I have a script, in which part of it calls to Snowflake on a query. That query is located in an .sql folder and two params are passed through to it in the script. For example:
#query.sql
select *
from {some_table}
where
date == '{hit_date}'
and
id in ({id_list}) or ({id_list}) is null)
And the piece of script acts like:
def run_query(hit_date, id_list):
conn = snowflake.connector.connect(**snowflake_creds)
cursor = conn.cursor()
with open(query.sql) as f:
query = f.read()
cursor.execute(query.format(hit_date, id_list))
The problem comes when the input for 'id_list' is 'None'. I am thrown this error:
snowflake.connector.errors.ProgrammingError: 000904 (42000): 019b2591-04da-7b13-0000-89bd25e0043a: SQL compilation error: error line 20 at position 42
invalid identifier 'NONE'
From my understanding Python's 'None' will automatically convert to SQL's 'Null'. Is this not the case? What should the input for 'id_list' be if 'None/Null'?
I was unable to find a clear answer for this specific error. However I was able to work around this by replacing the condition with an impossible value when no list is given.
#query.sql
select *
from {some_table}
where
date == '{hit_date}'
and
(id in ({id_list}) or impossible_value in ({id_list}))
I got some python code (psycopg2) with which should insert data into a database:
def debug(self):
try:
self.connection.execute(
"SELECT test();")
res = self.connection.fetchall()
print(res)
except Exception as e:
print(e)
return
The test() function in pgsql is this:
CREATE OR REPLACE FUNCTION test(
) RETURNS setof varchar
AS $Body$
BEGIN
INSERT INTO Linie(name) VALUES('3');
RETURN QUERY(SELECT * FROM linie);
END;
$Body$ LANGUAGE plpgsql;
When i change the "name" value and execute the query in pgAdmin there is a now entry in the database. However when calling the function from python it always overrides the value.
The table is defined as follows:
CREATE TABLE Linie(
name varchar,
PRIMARY KEY (name)
);
For example with pgAdmin i can insert 1,2,3,4,5.
With python after running 5 equivalent queries it is just 5.
Calling the test function with nodeJS works fine.
When calling the function once from python then changing the insert value and then calling it from python again, the values are not replaced but inserted.
Also it does not throw any errors and returns the table as it should (except the replaced value).
why is this happening and what can i do against it?
Psycopg2 by default will not commit changes made to the database unless you explicitly call connection.commit() after executing your SQL. You could alter you code like so:
def debug(self):
try:
self.connection.execute(
"SELECT test();")
res = self.connection.fetchall()
self.connection.commit()
print(res)
except Exception as e:
print(e)
return
However, please be careful doing this as I have no information on what exactly self.connection is an instance of, therefore I have assumed it to be of type connection :)
Alternatively, when you setup your connection to the DB, set the property autocommit to True, as documented here. Example:
self.connection = psycopg2.connect(user='foo', password='bar', host='localhost', dbname='mydb')
self.connection.autocommit = True
If you are already using autocommit let me know and I'll have another look at your question.
I am trying to set the lastupdated timestamp in db2 to current timestamp everytime the section of my script runs
datetimes = datetime.now()
datetime1 = datetimes.strftime("%Y-%m-%d %H:%M:%S")
datetime2 = datetime.strptime(datetime1,'%Y-%m-%d %H:%M:%S')
print(type(datetime2))
print(datetime2)
sql = "update salesorder set LASTUPDATEUSER = 'Testing' and LASTUPDATEDATETIME = ? where code ='0888' "
prepared = ibm_db.prepare(conn, sql)
returnCode = ibm_db.bind_param(prepared,1,datetime2, ibm_db.SQL_PARAM_INPUT,ibm_db.SQL_CHAR)
if returnCode == False:
print("unable to bind")
#ibm_db.execute(upstmt)
#param = param1
returnCode = ibm_db.execute(prepared)
if returnCode == False:
print("unable to execut")
on running this script I get the error
Traceback (most recent call last):
File "new.py", line 27, in <module>
returnCode = ibm_db.execute(prepared)
SQLCODE=-420atement Execute Failed: [IBM][CLI Driver][DB2/LINUXX8664] SQL0420N Invalid character found in a character string argument of the function "BOOLEAN". SQLSTATE=22018
I have tried multiple resolutions like passing the datetime as a string as well but no luck with solving the issue this is the timestamp format used in db2 '2020-02-21 13:37:37'
If someone can please provide a way to resolve this that would be really helpful
Your update stmt is wrong. Change:
update salesorder
set LASTUPDATEUSER = 'Testing' and LASTUPDATEDATETIME = ?
where code ='0888'
to:
update salesorder
set LASTUPDATEUSER = 'Testing'
, LASTUPDATEDATETIME = ?
where code ='0888'
or:
update salesorder
set (LASTUPDATEUSER, LASTUPDATEDATETIME) = ('Testing', ?)
where code ='0888'
The error message you get is puzzling, but is due to the fact that you are trying to evaluate a Boolean from a string:
db2 "values 'testing' and current_timestamp = current_timestamp"
1
--
SQL0420N Invalid character found in a character string argument of the
function "BOOLEAN". SQLSTATE=22018
compare with
db2 "values true and current_timestamp = current_timestamp"
1
--
1
even if that succeded you would get an error trying to assign a timestamp column a boolean value.
I guess older versions of Db2 would have objected during prepare, but nowadays type checking is much more relaxed, which leads to very counter intuitive error messages from time to time
That said, you may want to look into doing:
update salesorder
set (LASTUPDATEUSER, LASTUPDATEDATETIME) = ('Testing', current_timestamp)
where code ='0888'
Your code would then be reduced to:
sql = """update salesorder
set (LASTUPDATEUSER, LASTUPDATEDATETIME) = ('Testing', current_timestamp)
where code ='0888'"""
prepared = ibm_db.prepare(conn, sql)
returnCode = ibm_db.execute(prepared)
if returnCode == False:
print("unable to execut")
As a side node, I find it a bit cumbersome to explicitly bind parameters and usually just do:
ibm_db.execute(prepared, (param1, ...))
If the purpose is just to update the timestamp whenever the row is changed, concider a trigger:
create trigger trg1
no cascade before update on salesorder
referencing new as n
for each row
set n.LASTUPDATEDATETIME = current_timestamp
If you change your update statement to:
update salesorder
set LASTUPDATEUSER = 'Testing'
where code ='0888'
the trigger will do that for you:
db2 "select * from salesorder"
LASTUPDATEUSER LASTUPDATEDATETIME CODE
-------------------- -------------------------- -----
Testing 2020-04-11-11.51.22.055602 0888
1 record(s) selected.
./aa.py
db2 "select * from salesorder"
LASTUPDATEUSER LASTUPDATEDATETIME CODE
-------------------- -------------------------- -----
Testing 2020-04-11-11.58.50.222753 0888
1 record(s) selected.
Dependent on your version of Db2, you may also look into declaring the column as:
FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP
You probably want GENERATED ALWAYS, but I don't think you can alter the column to that, so you will have to do a bit more work to get that in place.
I'm trying to execute a stored procedure to query a table, and am having trouble passing through a parameter successfully.
title=cursor.execute("SELECT titlequery(%s)", str(member_id))`
titlequery() is created by this:
CREATE OR REPLACE FUNCTION public.titlequery(mid text)
RETURNS text AS
$BODY$
SELECT title FROM Member WHERE member_id=mid;
$BODY$
LANGUAGE sql
And the error I'm getting:
modules.pg8000.core.ProgrammingError: ('ERROR', '42P18', 'could not
determine data type of parameter $2', 'postgres.c', '1356',
'exec_parse_message', '', '')
Does anyone know what's happening here?
PEP-249 specifies API for database drivers and pg8000 follows this API as well
pg8000 is a DB-API 2.0 compatible pure-Python interface to the PostgreSQL database engine.
From PEP-249 execute method specification:
Parameters may be provided as sequence or mapping and will be bound to variables in the operation.
We can see at pg8000 sources an example of how to pass parameters to query.
So you should pass a tuple/list of values, not value itself.
Also we should execute query first and then fetch its results using fetchone or fetchmany or fetchall because execute itself returns None (more at sources). I guess OP needs one record, so we're going to use fetchone.
Note: fetchone method returns record represented as tuple, so if we need first coordinate, then we should get it using zero index.
In your case you should try:
parameters = (str(member_id),) # WARNING: don't miss the comma
cursor.execute("SELECT titlequery(%s)", parameters)
title = cursor.fetchone()[0]
or
parameters = [str(member_id)]
cursor.execute("SELECT titlequery(%s)", parameters)
title = cursor.fetchone()[0]
Example
This worked for me
import pg8000
table_definition = """
CREATE TABLE Member(
title VARCHAR(40) NOT NULL,
member_id VARCHAR(40) NOT NULL)
"""
procedure_definition = """
CREATE OR REPLACE FUNCTION public.titlequery(mid text)
RETURNS text AS
$BODY$
SELECT title FROM Member WHERE member_id=mid;
$BODY$
LANGUAGE sql
"""
connection = pg8000.connect(database='database',
user='username',
password='password',
host='hostname',
port=5432)
cursor = connection.cursor()
# Preparation
cursor.execute(table_definition)
cursor.execute(procedure_definition)
values = ('Information', 'A000042553')
cursor.execute('INSERT INTO Member (title, member_id) VALUES (%s, %s)',
values)
# Reading stored procedure result
parameters = ('A000042553',)
cursor.execute("SELECT titlequery(%s)", parameters)
title = cursor.fetchone()[0]
print(title)
# Cleanup
cursor.close()
connection.close()
gives us
Information