Welcome, guest | Sign In | My Account | Store | Cart
'''
Transparent use of prepared statements with Postgresql.

Usage example with psycgopg2: We create a cursor that provides
both with dict-like field access and prepared statements.

from psycopg2.extensions import connection as _connection
from psycopg2.extras import RealDictCursor
from this_recipe import PrepCursorMixin

class Cursor(PrepCursorMixin, RealDictCursor):
    pass


class Connection(_connection):
    def cursor(self):
        return super(Connection, self).cursor(cursor_factory=Cursor)


def connect(*a, **kw):
    return Connection(*a, **kw)
'''


import re

class PrepCursorMixin(object):
   
'''
    mix in with dbapi cursor class
   
    formatRe fishes out all format specifiers for a given paramstyle
    this one works with paramstyles '
format' or 'pyformat'
    '''

    formatRe
= re.compile('(\%s|\%\([\w\.]+\)s)', re.DOTALL)

   
def __init__(self, *a, **kw):
       
super(PrepCursorMixin, self).__init__(*a, **kw)

       
# preferably store prepd statements on connection
        conn
= getattr(self, 'connection', None)
       
if conn:
            pc
= getattr(conn, 'prepCache', {})
           
self.prepCache = self.connection.prepCache = pc
       
else:    
           
self.prepCache = {}

   
def executeps(self, cmd, args=None):
       
'''
        execute a command using a prepared statement.
        '''

        prepStmt
= self.prepCache.get(cmd)
       
if prepStmt is None:
            cmdId
= "ps_%d" % (len(self.prepCache) + 1)  
           
# unique name for new prepared statement
            prepStmt
= self.prepCache[cmd] = \
                       
self.prepareStatement(cmd, cmdId)

       
self.execute(prepStmt, args)

   
def prepareStatement(self, cmd, cmdId):
       
'''
        translate a sql command into its corresponding
        prepared statement, and execute the declaration.
        '''

        specifiers
= []

       
def replaceSpec(mo):
            specifiers
.append(mo.group())
           
return '$%d' % len(specifiers)

        replacedCmd
= self.formatRe.sub(replaceSpec, cmd)
        prepCmd
= 'prepare %s as %s' % (cmdId, replacedCmd)

       
if len(specifiers) == 0:    # no variable arguments
            execCmd
= 'execute %s' % cmdId

       
else:       # set up argument slots in prep statement
            execCmd
= 'execute %s(%s)' % (cmdId, ', '.join(specifiers))

       
self.execute(prepCmd)
       
return execCmd

   
def executemanyps(self, cmd, seq_of_parameters):
       
'''
        prepared statement version of executemany.
        '''

       
for p in seq_of_parameters:
           
self.executeps(cmd, p)

       
# Don't want to leave the value of the last execute() call
       
try:
           
self.rowcount = -1
       
except TypeError:   # fooks with psycopg
           
pass


if __name__ == '__main__':
   
'''
    just demonstrate the string mangling that goes on
    '''


   
class DummyBaseCursor(object):
       
def __init__(self, conn):
           
self.connection = conn

       
def execute(self, cmd, args=None):
           
print 'execute'
           
print 'cmd:', cmd
           
print 'args:', args
           
print '-' * 20

   
class DummyCursor(PrepCursorMixin, DummyBaseCursor):
       
def executeps(self, cmd, args):
           
print 'executeps'
           
print 'cmd:', cmd
           
print 'args:', args
           
super(DummyCursor, self).executeps(cmd, args)

   
class DummyConnection(object): pass

    dc
= DummyCursor(DummyConnection)

    sql
= \
       
['''
        select * from dummies
        where name=%s
        and surname=%s
        '''
,
       
'''
        select * from dummies
        where name=%(name)s
        and surname=%(surname)s
        '''
,
       
'select * from dummies']

    theargs
= [('Joe','Blow'), {'name':'Joe', 'surname':'Blow'}, None]

   
for x in range(3):
       
for y in range(2):
            dc
.executeps(sql[x], theargs[x])

History

  • revision 4 (15 years ago)
  • previous revisions are not available