def execproc(procname, engine, queryParams=[]):
""" Purpose:
executes a named stored procedure and returns the result.
Function takes 3 parameters, procname is a string containing the
name of the stored procedure.
engine is the sqlalchemy engine instance through which the query
will be executed.
queryParams contains the input parameters in a list form (if any).
description:
First it starts building a query string that commonly begins with
the common select * from syntax that is needed for calling a stored
procedure.
The code then goes to check if one or more parameters are
supplied. If yes then
a for loops runs that concatinate the parameters inside ()
During this process it checks the datatype of each supplied
parameter to stringify any parameter or keep it as integer.
This is done using the %s, %d and %f place holders.
After the query is built using the user input that consisted of the
proc name and parames, it executes the same using the supplied engine
instance.
The result of the execution contains the rows returned by the
stored procedure that was called.
"""
listCounter = 0
if len(queryParams) == 0:
queryString = "select * from %s() " % (procname)
else:
queryString = "select * from %s(" % (procname)
for param in queryParams:
if type(param) == str:
queryString = queryString + "'%s'" % (param)
if type(param) == int:
queryString = queryString + "%d" % (param)
if type(param) == float:
queryString = queryString + "%.2f" % (param)
if type(param) == NoneType:
queryString = queryString + "None"
if listCounter < (len(queryParams) - 1):
queryString = queryString + ","
listCounter = listCounter + 1
queryString = queryString + ")"
print queryString
res =
engine.execute(text(queryString).execution_options(autocommit=True))
return res
Thanks for help in advance.
Happy hacking.
Krishnakant.
> Hello all,
> I have an interesting problem for which I am sure some simple solution must be existing.
> I have made a Python function which I will paist below.
> Basically what the function does is that it takes 3 parameters, namely the name of a stored procedure, engine instance and a set of parameters.
> This function is used to make calls to postgresql based stored procedures in a modular way.
> The function is kept central and all my modules just pass the necessary parameters and leave it to the function to do the rest.
> I got it working perfectly, except that I don't know how to handle special characters when constructing the query that makes a call to a stored procedure. So if I have an insert query which has a value with a single quote ('), it crashes.
This is because the function is not using bound parameters. Dealing with individual datatypes and how they are formatted to the database is something you should let the DBAPI handle.
SQLAlchemy includes the capability to call functions built in via the "func" parameter. Your execproc could be written as:
from sqlalchemy import create_engine, func
engine = create_engine('mysql://root@localhost/test', echo=True)
def execproc(procname, engine, queryParams=[]):
function = getattr(func, procname)
function_with_params = function(*queryParams)
return engine.execute(function_with_params.execution_options(autocommit=True))
print execproc("concat", engine, ["dog", " ", "cat"]).scalar()
Hi Michael,
I tryed this code with postgresql (psycopg2 ).
I have a group table with the fields.
groupcode integer, groupname text and groupdesc text.
When I pass in a procedure name called get groups with all the above
listed columns as output parameters (actually the procedure returns
setof rows), I get the could not locate column on the groupcode field.
I had previously mailed you with a longish function called execProc
which you corrected and maild back.
With that long function without func, I never had this problem.
could you please point out what could have gone wrong?
Note that I am not paisting the function here as it is exactly the same
as you had provided.
I will however paist the line which gave me the error.
I will infact give you the code concerning the function where your
version of execProc was called.
#note that we have a dbconnect module containing an array of engines and
also the execProc method.
res = dbconnect.execproc("getAllGroups",dbconnect.engines[client_id])
#since we know that the function getAllGroups exists and that it returns
the exact 3 fields from the groups table,
#we are shure we have some records to process.
#We will loop through the generated resultset in res
#We wish to make a 2 dymentional list of rows and columns to be
transfered over an xmlrpc connection.
result = []
for row in res:
result.append([row["groupcode"],row["groupname"],row["groupdesc"]])
return result
I know for sure that the names of the fields used inside row[] are
absolutely correct, and the code actually works when I use the old
execProc which did not have all the sqlalchemy's func trickery.
Could you please explain?
happy hacking.
Krishnakant.
> On 11/02/12 21:10, Michael Bayer wrote:
>> def execproc(procname, engine, queryParams=[]):
>> function = getattr(func, procname)
>> function_with_params = function(*queryParams)
>> return engine.execute(function_with_params.execution_options(autocommit=True))
>>
>
>
> Hi Michael,
> I tryed this code with postgresql (psycopg2 ).
>
> I have a group table with the fields.
> groupcode integer, groupname text and groupdesc text.
>
> When I pass in a procedure name called get groups with all the above listed columns as output parameters (actually the procedure returns setof rows), I get the could not locate column on the groupcode field.
OK sorry, you want individual columns from the function which means "select *" from it. Here is that, using a function from postgresql's website:
from sqlalchemy import create_engine, func, select, literal_column
engine = create_engine('postgresql://scott:tiger@localhost/test', echo=True)
engine.execute("""
CREATE OR REPLACE FUNCTION dup(int) RETURNS TABLE(f1 int, f2 text)
AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
LANGUAGE SQL;
""")
def execproc(procname, engine, queryParams=[]):
function = getattr(func, procname)
function_with_params = function(*queryParams)
return engine.execute(
select([literal_column('*')]).\
select_from(function_with_params).\
execution_options(autocommit=True)
)
for row in execproc("dup", engine, [42]):
print row.f1, row.f2
On 12/02/12 22:01, Michael Bayer wrote:
def execproc(procname, engine, queryParams=[]):
conn = engine.connect()
try:
trans = conn.begin()
result = conn.execute(
"SELECT * FROM %s(%s)" % (
procname,
", ".join("%s" for arg in queryParams),
), queryParams
)
trans.commit()
return list(result)
finally:
conn.close()
still another, using the DBAPI directly. This uses only psycopg2 and the Python standard library:
import psycopg2
import collections
def execproc(procname, queryParams=[]):
conn = psycopg2.connect(user="scott", password="tiger",
host="localhost", database="test")
cursor = conn.cursor()
cursor.execute(
"SELECT * FROM %s(%s)" % (
procname,
", ".join("%s" for arg in queryParams),
), queryParams
)
conn.commit()
result = list(cursor)
conn.close()
tup = collections.namedtuple("row", [d[0] for d in cursor.description])
return [tup(*row) for row in result]
All three versions of this will execute any psycopg2 stored procedure, including ones that INSERT, UPDATE, or DELETE and return the results as a named result set. The result rows here are not quite the same thing as "out" parameters which are an Oracle concept but fortunately this is much easier to do than Oracle "out" parameters.
> --
> You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
> To post to this group, send email to sqlal...@googlegroups.com.
> To unsubscribe from this group, send email to sqlalchemy+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
>
Thanks a million for the splendid work and your most valued help.
happy hacking.
Krishnakant.
You could also say conn = engine.connect().execution_options(autocommit=True).
So does that mean I will have to use a connection object and not do the
execution with just engine?
Note that the engine parameter I pass to the execProc is already
connected to a database.
So do I need to create an additional connection object?
I thought that the engine had its own implesit connection which it uses
for executing the functions or any sql for that matter.
> You could also say conn = engine.connect().execution_options(autocommit=True).
>
Ah, meaning the connection object is just a formal requirement is it?
Happy hacking.
Krishnakant.