Executing SQL Server stored procedures with parameters in Python - python

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

Related

POSTGRES ALTER procedure syntax error I can't figure out

Here is the error:
ERROR: [GET_ALL_DATASETS_BY_DATE] syntax error at or near "LANGUAGE"
LINE 3: LANGUAGE SQL
Here is the code
def PROCEDURE_GET_ALL_DATASETS_BY_DATE(conn):
print("CREATING [GET_ALL_DATASETS_BY_DATE] PROCEDURE")
try:
cursor = conn.cursor()
cursor.execute(f"""
ALTER PROCEDURE GET_ALL_DATASETS_BY_DATE(how_many int)
LANGUAGE SQL
AS $$
SELECT U.username, F.File_PATH "Path", Description "Desc", F.Date_Time "Date", F.File_size "Size"
FROM USERS as U
INNER JOIN FILES as F
ON F.UserId = U.User_Id
ORDER BY F.Date_Time
limit how_many
$$;
""")
conn.commit()
except Exception as e:
cursor.execute("ROLLBACK")
print("ERROR: [GET_ALL_DATASETS_BY_DATE] " + str(e))
I feel like I am missing something super simple...Thank you for your time. Any help is appreciated
here is the documentation link to how I've been modelling my procedures.
https://www.postgresql.org/docs/current/sql-createprocedure.html
if you wanna bring the table by using join. You can use the code below.
PostgreSQL is not like MSSQL or TSQL. In MSSQL and TSQL we can use procedure as direct. But In Postgres, if we want to use select processing, we have to specify columns on our query.
Example:
CREATE OR REPLACE FUNCTION public.myposts(_user_id integer)
RETURNS TABLE(post_id integer, create_user_id integer, ad_name character varying, topic character varying, content_values text, slug_url character varying)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
select distinct posts.id,posts.create_user_id,users.ad_name,posts.topic,posts.content_values,posts.slug_url from posts
inner join users on users.id = posts.create_user_id
inner join post_receivers on post_receivers.post_id = posts.id
where post_receivers.user_id = _user_id;
END
$function$
;

psycpg2 Insert into fails

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.

How to implement DBMS_METADATA.GET_DDL in cx_Oracle and python3 and get the ddl of the table?

this is the oracle command i am using :-
query = '''SELECT DBMS_METADATA.GET_DDL('TABLE', 'MY_TABLE', 'MY_SCHEMA') FROM DUAL;'''
cur.execute(query)
now how to get the ddl of the table using cx_Oracle and python3 .
please help . i am unable to extract the ddl.
The following code can be used to fetch the contents of the DDL from dbms_metadata:
import cx_Oracle
conn = cx_Oracle.connect("username/password#hostname/myservice")
cursor = conn.cursor()
def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
if defaultType == cx_Oracle.CLOB:
return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize)
cursor.outputtypehandler = OutputTypeHandler
cursor.execute("select dbms_metadata.get_ddl('TABLE', :tableName) from dual",
tableName="THE_TABLE_NAME")
text, = cursor.fetchone()
print("DDL fetched of length:", len(text))
print(text)
The use of the output type handler is to eliminate the need to process the CLOB. Without it you would need to do str(lob) or lob.read() in order to get at its contents. Either way, however, you are not limited to 4,000 characters.
cx_Oracle has native support for calling PL/SQL.
Assuming you have connection and cursor object, the snippet will look like this:
binds = dict(object_type='TABLE', name='DUAL', schema='SYS')
ddl = cursor.callfunc('DBMS_METADATA.GET_DDL', keywordParameters=binds, returnType=cx_Oracle.CLOB)
print(ddl)
You don't need to split the DDL in 4k/32k chunks, as Python's str doesn't have this limitation. In order to get the DDL in one chunk, just set returnType to cx_Oracle.CLOB. You can later convert it to str by doing str(ddl).

unable to insert data that are stored in variables in to an MYSQL database table using python

am trying to insert the data entered into the web form into database table,i am passing the data to the function to insert the data,but it was not successful below is my code
def addnew_to_database(tid,pid,usid,address,status,phno,email,ord_date,del_date):
connection = mysql.connector.connect(user='admin_operations', password='mypassword',host='127.0.0.1',database='tracking_system')
try:
print tid,pid,usid,address,status,phno,email,ord_date,del_date
cursor = connection.cursor()
cursor.execute("insert into track_table (tid,pid,usid,address,status,phno,email,ord_date,del_date) values(tid,pid,usid,address,status,phno,email,ord_date,del_date)")
cursor.execute("insert into user_table (tid,usid) values(tid,usid)")
finally:
connection.commit()
connection.close()
You should pass the variables as an argument to .execute instead of putting them in the actual query. E.g.:
cursor.execute("""insert into track_table
(tid,pid,usid,address,status,phno,email,ord_date,del_date)
values (%s,%s,%s,%s,%s,%s,%s,%s,%s)""",
(tid,pid,usid,address,status,phno,email,ord_date,del_date))
cursor.execute("""insert into user_table
(tid,usid)
values (%s,%s)""",(tid,usid))
You should tell us what API you are using and what the error code is.
You should define the values within the execution, right now within the sql statement as a string they are not referencing anything.
Typically when you use a variable name inside of a sql statement this way, you need to indicate that it is a variable you are binding data to. This might be replacing it with (1,2,3,4..) or (%s,%s,...) that corresponds to an ordered list or using variable names (:tid,:pid,...) that you then define the values of with a dictionary as the second argument of execute().
Like this:
track_table_data = [tid,pid,usid,address,status,phno,email,ord_date,del_date]
user_table_data = [tid,usid]
cursor.execute("insert into track_table (tid,pid,usid,address,status,phno,email,ord_date,del_date) values(1,2,3,4,5,6,7,8,9)", track_table_data)
cursor.execute("insert into user_table (tid,usid) values(1,2)",user_table_data)
or
cursor.execute("insert into track_table (tid,pid,usid,address,status,phno,email,ord_date,del_date) values(:tid,:pid,:usid,:address,:status,:phno,:email,:ord_date,:del_date))", {'tid':tid,'pid':pid,'usid':usid,'address':address,'status':status,'phno':status,'email':email,'ord_date':ord_date,'del_date':del_date})
cursor.execute("insert into user_table (tid,usid) values(:tid,:usid)",{'tid':tid,'usid':usid})

django + south + python: strange behavior when using a text string received as a parameter in a function

this is my first question.
I'm trying to execute a SQL query in django (south migration):
from django.db import connection
# ...
class Migration(SchemaMigration):
# ...
def transform_id_to_pk(self, table):
try:
db.delete_primary_key(table)
except:
pass
finally:
cursor = connection.cursor()
# This does not work
cursor.execute('SELECT MAX("id") FROM "%s"', [table])
# I don't know if this works.
try:
minvalue = cursor.fetchone()[0]
except:
minvalue = 1
seq_name = table + '_id_seq'
db.execute('CREATE SEQUENCE "%s" START WITH %s OWNED BY "%s"."id"', [seq_name, minvalue, table])
db.execute('ALTER TABLE "%s" ALTER COLUMN id SET DEFAULT nextval("%s")', [table, seq_name + '::regclass'])
db.create_primary_key(table, ['id'])
# ...
I use this function like this:
self.transform_id_to_pk('my_table_name')
So it should:
Find the biggest existent ID or 0 (it crashes)
Create a sequence name
Create the sequence
Update the ID field to use sequence
Update the ID as PK
But it crashes and the error says:
File "../apps/accounting/migrations/0003_setup_tables.py", line 45, in forwards
self.delegation_table_setup(orm)
File "../apps/accounting/migrations/0003_setup_tables.py", line 478, in delegation_table_setup
self.transform_id_to_pk('accounting_delegation')
File "../apps/accounting/migrations/0003_setup_tables.py", line 20, in transform_id_to_pk
cursor.execute(u'SELECT MAX("id") FROM "%s"', [table.encode('utf-8')])
File "/Library/Python/2.6/site-packages/django/db/backends/util.py", line 19, in execute
return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: relation "E'accounting_delegation'" does not exist
LINE 1: SELECT MAX("id") FROM "E'accounting_delegation'"
^
I have shortened the file paths for convenience.
What does that "E'accounting_delegation'" mean? How could I get rid of it?
Thank you!
Carlos.
The problem is that you're using DB-API parameterization for things that are not SQL data. When you do something like:
cursor.execute('INSERT INTO table_foo VALUES (%s, %s)', (col1, col2))
the DB-API module (django's frontend for whatever database you are using, in this case) will know to escape the contents of 'col1' and 'col2' appropriately, and replace the %s's with them. Note that there are no quotes around the %s's. But that only works for SQL data, not for SQL metadata, such as table names and sequence names, because they need to be quoted differently (or not at all.) When you do
cursor.execute('INSERT INTO "%s" VALUES (%s, %s)', (tablename, col1, col2))
the tablename gets quoted as if you mean it to be string data to insert, and you end up with, for example, "'table_foo'". You need to separate your SQL metadata, which is part of the query, and your SQL data, which is not, like so:
sql = 'INSERT INTO TABLE "%s" VALUES (%%s, %%s)' % (tablename,)
cursor.execute(sql, (col1, col2))
Note that because the django DB-API frontend's paramstyle is 'pyformat' (it uses %s for placeholders) you need to escape those when you do the string formatting to create the SQL you want to execute. And note that this isn't secure against SQL injection attacks when you take the tablename from an insecure source and don't validate it.

Categories