diff --git a/postgresql/__init__.py b/postgresql/__init__.py index 18031a4e..2604a12d 100644 --- a/postgresql/__init__.py +++ b/postgresql/__init__.py @@ -22,13 +22,13 @@ ] __author__ = "James William Pye " -__date__ = "2009-12-14 21:16:00-07" +__date__ = "2009-01-03 16:53:00-07" __project__ = 'py-postgresql' __project_id__ = 'http://python.projects.postgresql.org' #: The py-postgresql version tuple. -version_info = (0, 9, 3, 'dev', 0) +version_info = (0, 9, 4, 'dev', 0) #: The py-postgresql version string. version = __version__ = '.'.join(map(str, version_info[:3])) + ( @@ -74,6 +74,11 @@ def open(iri = None, prompt_title = None, **kw): iri_params = {} std_params = _pg_param.collect(prompt_title = None) + # If unix is specified, it's going to conflict with any standard + # settings, so remove them right here. + if 'unix' in kw or 'unix' in iri_params: + std_params.pop('host', None) + std_params.pop('port', None) params = _pg_param.normalize( list(_pg_param.denormalize_parameters(std_params)) + \ list(_pg_param.denormalize_parameters(iri_params)) + \ diff --git a/postgresql/bin/pg_python.py b/postgresql/bin/pg_python.py index 583a7f70..bfe35e19 100644 --- a/postgresql/bin/pg_python.py +++ b/postgresql/bin/pg_python.py @@ -50,6 +50,10 @@ def command(argv = sys.argv): else: cat = None + trace_file = None + if co.pq_trace is not None: + trace_file = open(co.pq_trace, 'a') + try: need_prompt = False cond = None @@ -67,6 +71,8 @@ def command(argv = sys.argv): raise SystemExit(1) connector = pg_driver.fit(category = cat, **cond) connection = connector() + if trace_file is not None: + connection.tracer = trace_file.write connection.connect() except pg_exc.ClientCannotConnectError as err: for att in connection.failures: @@ -105,13 +111,8 @@ def command(argv = sys.argv): builtins_d = __builtins__ restore = {k : builtins_d.get(k) for k in builtin_overload} - trace_file = None - if co.pq_trace is not None: - trace_file = open(co.pq_trace, 'a') builtins_d.update(builtin_overload) try: - if trace_file is not None: - connection.tracer = trace_file.write with connection: rv = pythonexec( context = pycmd.postmortem(os.environ.get('PYTHON_POSTMORTEM')) diff --git a/postgresql/documentation/changes.txt b/postgresql/documentation/changes.txt index e83ae274..f8d17012 100644 --- a/postgresql/documentation/changes.txt +++ b/postgresql/documentation/changes.txt @@ -1,8 +1,20 @@ Changes ======= -0.9.3 ------ +0.9.4 in development +-------------------- + + * Fix handling of unix domain sockets by pg.open and driver.connect. + [Reported by twitter.com/rintavarustus] + * Fix db.tracer and pg_python's --pq-trace= + * Fix typo/dropped parts of a raise LoadError in .lib. + [Reported by Vlad Pranskevichus] + * Fix count return from .first() method. Failed to provide an empty + tuple for the rformats of the bind statement. + [Reported by dou dou] + +0.9.3 released on 2010-01-01 +---------------------------- * Fix inconsistency in savepoint identification by pq3.Transaction. Savepoints via db.xact() were broken. diff --git a/postgresql/documentation/clientparameters.txt b/postgresql/documentation/clientparameters.txt index 895b4433..e85bd675 100644 --- a/postgresql/documentation/clientparameters.txt +++ b/postgresql/documentation/clientparameters.txt @@ -199,6 +199,8 @@ systems it is ``"%APPDATA%\postgresql"`` [PGDATA] is *not* an environment variable. +.. _pg_envvars: + PostgreSQL Environment Variables ================================ @@ -227,6 +229,8 @@ The following is a list of environment variables that will be collected by the ===================== ====================================== +.. _pg_passfile: + PostgreSQL Password File ======================== diff --git a/postgresql/documentation/driver.txt b/postgresql/documentation/driver.txt index 12f5f31f..224c6d40 100644 --- a/postgresql/documentation/driver.txt +++ b/postgresql/documentation/driver.txt @@ -110,11 +110,8 @@ successful connection, there is no need to pass anything to open:: For a complete list of keywords that `postgresql.open` can accept, see `Connection Keywords`_. -For more information about the environment variables, see :doc:`Environment -Variables`. -For more information about the ``pgpassfile``, see :doc:`PostgreSQL Password -File`. - +For more information about the environment variables, see :ref:`pg_envvars`. +For more information about the ``pgpassfile``, see :ref:`pg_passfile`. `postgresql.driver.connect` --------------------------- @@ -185,7 +182,8 @@ a connection: Connect to a single IPv6 addressed host. ``postgresql.driver.default.unix(...)`` - Connect to a single unix domain socket. + Connect to a single unix domain socket. Requires the ``unix`` keyword which + must be an absolute path to the unix domain socket to connect to. ``host`` is the usual connector used to establish a connection:: @@ -241,6 +239,11 @@ interfaces: ``port`` The port on the host to connect to. + ``unix`` + The unix domain socket to connect to. Exclusive with ``host`` and ``port``. + Expects a string containing the absolute path to the unix domain socket to + connect to. + ``settings`` A dictionary or key-value pair sequence stating the parameters to give to the database. These settings are included in the startup packet, and should be @@ -1104,11 +1107,10 @@ Of course, more than one column can be transformed:: >>> row.transform(None, str, str) ('XX9301423', '2', '4.92') -More advanced usage can make use of `postgresql.python.functools.Composition` -or lambdas for compound transformations in a single pass of the row:: +More advanced usage can make use of lambdas for compound transformations in a +single pass of the row:: - >>> from postgresql.python.functools import Composition as compose - >>> strip_and_int = compose((stripxx, int)) + >>> strip_and_int = lambda x: int(stripxx(x)) >>> row.transform(strip_and_int) (9301423, 2, Decimal("4.92")) diff --git a/postgresql/driver/pq3.py b/postgresql/driver/pq3.py index ce471ace..688f8a05 100644 --- a/postgresql/driver/pq3.py +++ b/postgresql/driver/pq3.py @@ -982,7 +982,7 @@ def first(self, *parameters): self._pq_statement_id, self._input_formats, params, - self._output_formats, + self._output_formats or (), ), # Get all element.Execute(b'', 0xFFFFFFFF), @@ -1851,6 +1851,8 @@ def connect(self): sf, self.connector._startup_parameters, password = self.connector._password, ) + if hasattr(self, 'tracer'): + pq.tracer = self.tracer # Grab the negotiation transaction before # connecting as it will be needed later if successful. neg = pq.xact @@ -2290,7 +2292,7 @@ def __init__(self, unix = None, **kw): raise TypeError("'unix' is a required keyword and cannot be 'None'") self.unix = unix # constant socket connector - self._socketcreator = SocketCreator( + self._socketcreator = SocketFactory( (socket.AF_UNIX, socket.SOCK_STREAM), self.unix ) self._socketcreators = (self._socketcreator,) diff --git a/postgresql/iri.py b/postgresql/iri.py index c0da2307..b0d2bab3 100644 --- a/postgresql/iri.py +++ b/postgresql/iri.py @@ -42,7 +42,7 @@ def structure(d, fieldproc = ri.unescape): if host.startswith('[') and host.endswith(']'): host = host[1:-1] if host.startswith('unix:'): - cpd['unix'] = host[len('unix:'):] + cpd['unix'] = host[len('unix:'):].replace(':','/') else: cpd['host'] = host[1:-1] else: @@ -115,7 +115,7 @@ def construct(x, obscure_password = False): port = None if 'unix' in x: - host = '[unix:/' + x['unix'] + ']' + host = '[unix:' + x['unix'].replace('/',':') + ']' # ignore port.. it's a mis-config. elif 'host' in x: host = x['host'] diff --git a/postgresql/lib/__init__.py b/postgresql/lib/__init__.py index 8144ac4b..11c73c37 100644 --- a/postgresql/lib/__init__.py +++ b/postgresql/lib/__init__.py @@ -431,7 +431,7 @@ def load(libref): for x in find_libsql(libref, pg_sys.libpath): break else: - raise LoadError("library %r not in postgresql.sys.libpath") + raise pg_exc.LoadError("library %r not in postgresql.sys.libpath" % (libref,)) lib = ILF.open(x) except pg_exc.LoadError: raise diff --git a/postgresql/test/test_connect.py b/postgresql/test/test_connect.py index 4f78c4d1..8be8360c 100644 --- a/postgresql/test/test_connect.py +++ b/postgresql/test/test_connect.py @@ -15,7 +15,7 @@ class test_connect(pg_unittest.TestCaseWithCluster): """ - postgresql.driver *interface* tests. + postgresql.driver connectivity tests """ ip6 = '::1' ip4 = '127.0.0.1' @@ -27,10 +27,15 @@ def __init__(self, *args, **kw): super().__init__(*args,**kw) # 8.4 nixed this. self.do_crypt = self.cluster.installation.version_info < (8,4) + self.do_unix = sys.platform != msw def configure_cluster(self): super().configure_cluster() - self.cluster.settings['log_min_messages'] = 'log' + self.cluster.settings.update({ + 'log_min_messages' : 'log', + 'unix_socket_directory' : self.cluster.data_directory, + }) + # Configure the hba file with the supported methods. with open(self.cluster.hba_file, 'w') as hba: hosts = ['0.0.0.0/0', '0::0/0',] @@ -43,6 +48,7 @@ def configure_cluster(self): m = m )]) # trusted + hba.writelines(["local all all trust\n"]) hba.writelines(["host test trusted 0.0.0.0/0 trust\n"]) hba.writelines(["host test trusted 0::0/0 trust\n"]) # admin lines @@ -335,5 +341,34 @@ def test_trusted_connect(self): with c: self.failUnlessEqual(c.prepare('select current_user').first(), 'trusted') + def test_Unix_connect(self): + if msw: + return + unix_domain_socket = os.path.join( + self.cluster.data_directory, + '.s.PGSQL.' + self.cluster.settings['port'] + ) + C = pg_driver.default.unix( + user = 'test', + unix = unix_domain_socket, + ) + with C() as c: + self.failUnlessEqual(c.prepare('select 1').first(), 1) + self.failUnlessEqual(c.client_address, None) + + def test_pg_open_unix(self): + if msw: + return + unix_domain_socket = os.path.join( + self.cluster.data_directory, + '.s.PGSQL.' + self.cluster.settings['port'] + ) + with pg_open(unix = unix_domain_socket, user = 'test') as c: + self.failUnlessEqual(c.prepare('select 1').first(), 1) + self.failUnlessEqual(c.client_address, None) + with pg_open('pq://test@[unix:' + unix_domain_socket.replace('/',':') + ']') as c: + self.failUnlessEqual(c.prepare('select 1').first(), 1) + self.failUnlessEqual(c.client_address, None) + if __name__ == '__main__': unittest.main() diff --git a/postgresql/test/test_driver.py b/postgresql/test/test_driver.py index 3da5f6cf..eb10ebaa 100644 --- a/postgresql/test/test_driver.py +++ b/postgresql/test/test_driver.py @@ -407,6 +407,20 @@ def testStatementCall(self): ps = self.db.prepare("SELECT 1, 2 UNION ALL SELECT 3, 4") self.failUnlessEqual(ps(), [(1,2),(3,4)]) + def testStatementFirstDML(self): + self.db.execute("CREATE TEMP TABLE first (i int)") + fins = self.db.prepare("INSERT INTO first VALUES (123)").first + fupd = self.db.prepare("UPDATE first SET i = 321 WHERE i = 123").first + fdel = self.db.prepare("DELETE FROM first").first + self.failUnlessEqual(fins(), 1) + self.failUnlessEqual(fdel(), 1) + self.failUnlessEqual(fins(), 1) + self.failUnlessEqual(fupd(), 1) + self.failUnlessEqual(fins(), 1) + self.failUnlessEqual(fins(), 1) + self.failUnlessEqual(fupd(), 2) + self.failUnlessEqual(fdel(), 3) + def testStatementRowsPersistence(self): # validate that rows' cursor will persist beyond a transaction. ps = self.db.prepare("SELECT i FROM generate_series($1::int, $2::int) AS g(i)")