Re: autocommit property broken with MySQL

Matthew Bogosian <[email protected]> Wed, 17 Aug 2005 15:32:05 -0700
Newsgroups gmane.comp.web.skunkweb
Message-ID <[email protected]>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Aug 17, 2005, at 08:30, Jacob Smullyan wrote:

> I checked something in just now to support this kind of thing, but I
> didn't use your patch as is, because I don't think it works correctly
> with pools....
>
> Also, rather than using initSQL, I accept a parameter, "init", to
> initAlias, which can be a string or a callable.  If it is a callable,
> it should accept one argument, the new connection.  If it is a string,
> it is turned into a callable that executes the string as sql on the
> connection.  (I forgot for a moment that you accepted a list of
> strings, which is a good idea, and I'll go back and add that.)

This seems better than my approach. One could do something like this:

	# Generic DB init callable base class
	class DbInit(object):
		def __init__(self, *a_args, **a_kwargs):
			self.__args = a_args
			self.__kwargs = a_kwargs
		def __call__(self, a_conn):
			self.init(a_conn, *self.__args, **self.__kwargs)
		def init(self, a_conn, *a_args, **a_kwargs):
			pass # Overload this method in derived classes

If you want autocommit initialization behavior which is specific to 
each DB type, you could define an init class abstracting it:

	class MySQLInit(DbInit):
		def init(self, a_conn, a_autocommit = False):
			a_conn.execute('SET AUTOCOMMIT = %s', \
				int(bool(a_autocommit)))

	class PsychoPGInit(DbInit):
		def init(self, a_conn, a_autocommit = False):
			a_conn.conn.isolation_level = not a_autocommit

	pydo.initAlias('mysql_db', 'mysql', ..., \
		init = MySQLInit(a_autocommit = False))

	pydo.initAlias('pgsql_db', 'psycopg', ..., \
		init = PsychoPGInit(a_autocommit = False))

If this approach is too complicated for most (one callable could accept 
and execute many statements), one could also accept either a string, a 
callable, a list of strings or callables:

	if init:
		if isinstance(init, basestring) \
	        or callable(init):
			init = [init]
		for i in xrange(len(init)):
			if isinstance(init[i], basestring):
				sql = init[i]
				def _init(conn):
					c=conn.cursor()
					c.execute(sql)
					c.close()
				init[i] = _init
			...

Alternatively, one could require that init be a sequence of the format 
(init0, init1, ..., initN) where initI could be either a SQL string, a 
sequence (sql_string, (value0, value1, ..., valueN)), a sequence 
(sql_string, {key0: value0, key1: value1, ..., keyN: valueN}), or a 
callable. One could even use a callable to implement such a behavior by 
deriving from DbInit (above) as such:

	class SQLInit(DbInit):
		def init(self, a_conn, a_exec, *a_vals, **a_kwvals):
			execs = a_exec
			if isinstance(execs, basestring):
				execs = [[execs]]
				if a_vals:
					execs[0].append(a_vals)
				elif a_kwvals:
					execs[0].append(a_kwvals)
			for i in xrange(len(execs)):
				exec_val = execs[i]
				if isinstance(exec_val, basestring) \
					or operator.isSequenceType(exec_val):
					if isinstance(exec_val, basestring):
						exec_val = [exec_val, None]
					sql = exec_val[0]
					sql_vals = exec_val[1:]
					def _init(conn):
						c=conn.cursor()
						if sql_vals:
							c.execute(sql, sql_vals[0])
						else:
							c.execute(sql)
						c.close()
					execs[i] = _init
					del _init, exec_val, sql, sql_vals
				elif not callable(exec_val):
					raise ValueError, ...
			for callable in execs:
				callable(a_conn)
	init_seq = \
	[
		'SET AUTOCOMMIT = 0',
		(SET AUTOCOMMIT = %s', (0,)),
		(SET AUTOCOMMIT = %(val)s', {'val': 0}),
		MySQLInit(a_autocommit = False),
	]
	initAlias('mysql_db', 'mysql', ..., \
		init = SQLInit(init_seq))
	initAlias('pgsql_db', 'psychopg', ..., \
		init = SQLInit('SELECT * FROM %s', 'my_table');

This is probably a little too complicated that what you had in mind, 
but at least your current implementation allows for it (even if it is a 
fair amount of work for the user to do things like the above).

> I've only verified that the regression tests still work; I don't know
> if the initialization works yet.  Let me know if you try it.  Also, I
> still have been putting off fixing autocommit, for some peculiar and
> no doubt pathological psycological non-reason.

I don't think it hurts anything leaving autocommit in there (even if it 
is broken), especially since removing it would probably upset the fact 
that this is a "beta" and (in theory) should have its API frozen by 
now.

	-- Matt

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.0 (Darwin)

iD8DBQFDA7rlnLpDzL5I7l8RAquxAJ9ql06M3ltsxd1qt4n9l+b1ctCwGwCfbR8n
dPkP43GHbGubDKC9Bjl65gk=
=FCXL
-----END PGP SIGNATURE-----



-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf