Fixed crash and win32 patches.

This commit is contained in:
Federico Di Gregorio 2007-04-11 04:16:00 +00:00
parent d6f2aa27b7
commit 2a6b523506
5 changed files with 200 additions and 43 deletions

View File

@ -1,3 +1,11 @@
2007-04-11 Federico Di Gregorio <fog@initd.org>
* psycopg/cursor_type.c: added check to raise an error when
some crazy programmer tries to use different argument formats
in the same query string. Fixes #162.
* Applied patch from David Rushby to fix win32 builds.
2007-04-10 Federico Di Gregorio <fog@initd.org> 2007-04-10 Federico Di Gregorio <fog@initd.org>
* Applied patch from David Rushby to fix mem and ref leaks in * Applied patch from David Rushby to fix mem and ref leaks in

View File

@ -52,6 +52,17 @@ static void Dprintf(const char *fmt, ...) {}
#if defined(_WIN32) || defined(__BEOS__) #if defined(_WIN32) || defined(__BEOS__)
#ifdef _WIN32 #ifdef _WIN32
/* A Python extension should be linked to only one C runtime: the same one as
* the Python interpreter itself. Straightforwardly using the strdup function
* causes MinGW to implicitly link to the msvcrt.dll, which is not appropriate
* for any Python version later than 2.3.
* Microsoft C runtimes for Windows 98 and later make a _strdup function
* available, which replaces the "normal" strdup. If we replace all psycopg
* calls to strdup with calls to _strdup, MinGW no longer implicitly links to
* the obsolete C runtime. */
#define strdup _strdup
#include <winsock2.h> #include <winsock2.h>
#define pthread_mutex_t HANDLE #define pthread_mutex_t HANDLE
#define pthread_condvar_t HANDLE #define pthread_condvar_t HANDLE
@ -102,7 +113,7 @@ static struct tm *localtime_r(time_t *t, struct tm *tm)
#define inline #define inline
#endif #endif
#if defined(__FreeBSD__) || defined(_WIN32) || defined(__sun__) #if defined(__FreeBSD__) || (defined(_WIN32) && !defined(__GNUC__)) || defined(__sun__)
/* what's this, we have no round function either? */ /* what's this, we have no round function either? */
static double round(double num) static double round(double num)
{ {

View File

@ -79,7 +79,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
PyObject *key, *value, *n, *item; PyObject *key, *value, *n, *item;
char *d, *c; char *d, *c;
Py_ssize_t index = 0; Py_ssize_t index = 0;
int force = 0; int force = 0, kind = 0;
/* from now on we'll use n and replace its value in *new only at the end, /* from now on we'll use n and replace its value in *new only at the end,
just before returning. we also init *new to NULL to exit with an error just before returning. we also init *new to NULL to exit with an error
@ -88,6 +88,15 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
c = PyString_AsString(fmt); c = PyString_AsString(fmt);
while(*c) { while(*c) {
/* check if some crazy guy mixed formats */
if (kind == 2) {
Py_XDECREF(n);
psyco_set_error(ProgrammingError, (PyObject*)conn,
"argument formats can't be mixed", NULL, NULL);
return -1;
}
kind = 1;
/* handle plain percent symbol in format string */ /* handle plain percent symbol in format string */
if (c[0] == '%' && c[1] == '%') { if (c[0] == '%' && c[1] == '%') {
c+=2; force = 1; c+=2; force = 1;
@ -182,6 +191,15 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new)
because we don't need to check the old/new dictionary for because we don't need to check the old/new dictionary for
keys */ keys */
/* check if some crazy guy mixed formats */
if (kind == 1) {
Py_XDECREF(n);
psyco_set_error(ProgrammingError, (PyObject*)conn,
"argument formats can't be mixed", NULL, NULL);
return -1;
}
kind = 2;
value = PySequence_GetItem(var, index); value = PySequence_GetItem(var, index);
/* value has refcnt inc'ed by 1 here */ /* value has refcnt inc'ed by 1 here */

View File

@ -1,5 +1,5 @@
[build_ext] [build_ext]
define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3 define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,PSYCOPG_NEW_BOOLEAN,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3,PSYCOPG_DEBUG
# PSYCOPG_EXTENSIONS enables extensions to PEP-249 (you really want this) # PSYCOPG_EXTENSIONS enables extensions to PEP-249 (you really want this)
# PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower) # PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower)
# HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4 # HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4

200
setup.py
View File

@ -13,13 +13,13 @@
# for more details. # for more details.
"""Python-PostgreSQL Database Adapter """Python-PostgreSQL Database Adapter
psycopg is a PostgreSQL database adapter for the Python programming psycopg is a PostgreSQL database adapter for the Python programming
language. This is version 2, a complete rewrite of the original code to language. This is version 2, a complete rewrite of the original code to
provide new-style classes for connection and cursor objects and other sweet provide new-style classes for connection and cursor objects and other sweet
candies. Like the original, psycopg 2 was written with the aim of being candies. Like the original, psycopg 2 was written with the aim of being
very small and fast, and stable as a rock. very small and fast, and stable as a rock.
psycopg is different from the other database adapter because it was psycopg is different from the other database adapter because it was
designed for heavily multi-threaded applications that create and destroy designed for heavily multi-threaded applications that create and destroy
lots of cursors and make a conspicuous number of concurrent INSERTs or lots of cursors and make a conspicuous number of concurrent INSERTs or
@ -44,8 +44,10 @@ Operating System :: Unix
""" """
import os import os
import os.path
import sys import sys
import popen2 import popen2
import ConfigParser
from distutils.core import setup, Extension from distutils.core import setup, Extension
from distutils.errors import DistutilsFileError from distutils.errors import DistutilsFileError
from distutils.command.build_ext import build_ext from distutils.command.build_ext import build_ext
@ -55,6 +57,8 @@ from distutils.ccompiler import get_default_compiler
PSYCOPG_VERSION = '2.0.6b1' PSYCOPG_VERSION = '2.0.6b1'
version_flags = [] version_flags = []
PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win')
# to work around older distutil limitations # to work around older distutil limitations
if sys.version < '2.2.3': if sys.version < '2.2.3':
from distutils.dist import DistributionMetadata from distutils.dist import DistributionMetadata
@ -67,86 +71,127 @@ def get_pg_config(kind, pg_config="pg_config"):
if not r: if not r:
raise Warning(p[2].readline()) raise Warning(p[2].readline())
return r return r
class psycopg_build_ext(build_ext): class psycopg_build_ext(build_ext):
"""Conditionally complement the setup.cfg options file. """Conditionally complement the setup.cfg options file.
This class configures the include_dirs, libray_dirs, libraries This class configures the include_dirs, libray_dirs, libraries
options as required by the system. Most of the configuration happens options as required by the system. Most of the configuration happens
in finalize_options() method. in finalize_options() method.
If you want to set up the build step for a peculiar platform, add a If you want to set up the build step for a peculiar platform, add a
method finalize_PLAT(), where PLAT matches your sys.platform. method finalize_PLAT(), where PLAT matches your sys.platform.
""" """
user_options = build_ext.user_options[:] user_options = build_ext.user_options[:]
user_options.extend([ user_options.extend([
('use-pydatetime', None, ('use-pydatetime', None,
"Use Python datatime objects for date and time representation."), "Use Python datatime objects for date and time representation."),
('pg-config=', None, ('pg-config=', None,
"The name of the pg_config binary and/or full path to find it"), "The name of the pg_config binary and/or full path to find it"),
('use-decimal', None, ('use-decimal', None,
"Use Decimal type even on Python 2.3 if the module is provided."), "Use Decimal type even on Python 2.3 if the module is provided."),
]) ])
boolean_options = build_ext.boolean_options[:] boolean_options = build_ext.boolean_options[:]
boolean_options.extend(('use-pydatetime', 'use-decimal')) boolean_options.extend(('use-pydatetime', 'use-decimal'))
DEFAULT_PG_CONFIG = "pg_config" DEFAULT_PG_CONFIG = "pg_config"
def initialize_options(self): def initialize_options(self):
build_ext.initialize_options(self) build_ext.initialize_options(self)
self.use_pg_dll = 1 self.use_pg_dll = 1
self.pgdir = None self.pgdir = None
self.pg_config = self.DEFAULT_PG_CONFIG
self.mx_include_dir = None self.mx_include_dir = None
self.pg_config = self.autodetect_pg_config_path()
def get_compiler(self): def get_compiler(self):
"""Return the c compiler to compile extensions. """Return the name of the C compiler used to compile extensions.
If a compiler was not explicitely set (on the command line, for If a compiler was not explicitely set (on the command line, for
example), fall back on the default compiler. example), fall back on the default compiler.
""" """
return self.compiler or get_default_compiler() if self.compiler:
# distutils doesn't keep the type of self.compiler uniform; we
# compensate:
if isinstance(self.compiler, str):
name = self.compiler
else:
name = self.compiler.compiler_type
else:
name = get_default_compiler()
return name
def get_pg_config(self, kind): def get_pg_config(self, kind):
return get_pg_config(kind, self.pg_config) return get_pg_config(kind, self.pg_config)
def build_extensions(self):
# Linking against this library causes psycopg2 to crash
# on Python >= 2.4. Maybe related to strdup calls, cfr.
# http://mail.python.org/pipermail/distutils-sig/2005-April/004433.html
if self.get_compiler().compiler_type == "mingw32" \
and 'msvcr71' in self.compiler.dll_libraries:
self.compiler.dll_libraries.remove('msvcr71')
build_ext.build_extensions(self)
def finalize_win32(self): def finalize_win32(self):
"""Finalize build system configuration on win32 platform.""" """Finalize build system configuration on win32 platform."""
import struct
sysVer = sys.version_info[:2]
# Add compiler-specific arguments:
extra_compiler_args = []
compiler_name = self.get_compiler().lower()
compiler_is_msvc = compiler_name.startswith('msvc')
compiler_is_mingw = compiler_name.startswith('mingw')
if compiler_is_msvc:
# If we're using MSVC 7.1 or later on a 32-bit platform, add the
# /Wp64 option to generate warnings about Win64 portability
# problems.
if sysVer >= (2,4) and struct.calcsize('P') == 4:
extra_compiler_args.append('/Wp64')
elif compiler_is_mingw:
# Default MinGW compilation of Python extensions on Windows uses
# only -O:
extra_compiler_args.append('-O3')
# GCC-compiled Python on non-Windows platforms is built with strict
# aliasing disabled, but that must be done explicitly on Windows to
# avoid large numbers of warnings for perfectly idiomatic Python C
# API code.
extra_compiler_args.append('-fno-strict-aliasing')
# Force correct C runtime library linkage:
if sysVer <= (2,3):
# Yes: 'msvcr60', rather than 'msvcrt', is the correct value
# on the line below:
self.libraries.append('msvcr60')
elif sysVer in ((2,4), (2,5)):
self.libraries.append('msvcr71')
# Beyond Python 2.5, we take our chances on the default C runtime
# library, because we don't know what compiler those future
# versions of Python will use.
for exten in ext: # ext is a global list of Extension objects
exten.extra_compile_args.extend(extra_compiler_args)
# End of add-compiler-specific arguments section.
self.libraries.append("ws2_32") self.libraries.append("ws2_32")
self.libraries.append("advapi32") self.libraries.append("advapi32")
if self.get_compiler() == "msvc": if compiler_is_msvc:
# MSVC requires an explicit "libpq" # MSVC requires an explicit "libpq"
self.libraries.remove("pq") self.libraries.remove("pq")
self.libraries.append("libpq") self.libraries.append("libpq")
self.libraries.append("shfolder") self.libraries.append("shfolder")
for path in self.library_dirs: for path in self.library_dirs:
if os.path.isfile(os.path.join(path, "ms", "libpq.lib")): if os.path.isfile(os.path.join(path, "ms", "libpq.lib")):
self.library_dirs.append(os.path.join(path, "ms")) self.library_dirs.append(os.path.join(path, "ms"))
break break
def finalize_darwin(self): def finalize_darwin(self):
"""Finalize build system configuration on darwin platform.""" """Finalize build system configuration on darwin platform."""
self.libraries.append('ssl') self.libraries.append('ssl')
self.libraries.append('crypto') self.libraries.append('crypto')
def finalize_options(self): def finalize_options(self):
"""Complete the build system configuation.""" """Complete the build system configuation."""
build_ext.finalize_options(self) build_ext.finalize_options(self)
self.include_dirs.append(".") self.include_dirs.append(".")
self.libraries.append("pq") self.libraries.append("pq")
try: try:
self.library_dirs.append(self.get_pg_config("libdir")) self.library_dirs.append(self.get_pg_config("libdir"))
self.include_dirs.append(self.get_pg_config("includedir")) self.include_dirs.append(self.get_pg_config("includedir"))
@ -168,10 +213,86 @@ class psycopg_build_ext(build_ext):
else: else:
sys.stderr.write("Error: %s" % str(w)) sys.stderr.write("Error: %s" % str(w))
sys.exit(1) sys.exit(1)
if hasattr(self, "finalize_" + sys.platform): if hasattr(self, "finalize_" + sys.platform):
getattr(self, "finalize_" + sys.platform)() getattr(self, "finalize_" + sys.platform)()
def autodetect_pg_config_path(self):
res = None
if PLATFORM_IS_WINDOWS:
res = self.autodetect_pg_config_path_windows()
return res or self.DEFAULT_PG_CONFIG
def autodetect_pg_config_path_windows(self):
# Find the first PostgreSQL installation listed in the registry and
# return the full path to its pg_config utility.
#
# This autodetection is performed *only* if the following conditions
# hold:
#
# 1) The pg_config utility is not already available on the PATH:
if os.popen('pg_config').close() is None: # .close()->None == success
return None
# 2) The user has not specified any of the following settings in
# setup.cfg:
# - pg_config
# - include_dirs
# - library_dirs
for settingName in ('pg_config', 'include_dirs', 'library_dirs'):
try:
val = parser.get('build_ext', settingName)
except ConfigParser.NoOptionError:
pass
else:
if val.strip() != '':
return None
# end of guard conditions
import _winreg
pg_inst_base_dir = None
pg_config_path = None
reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
pg_inst_list_key = _winreg.OpenKey(reg,
'SOFTWARE\\PostgreSQL\\Installations'
)
try:
# Determine the name of the first subkey, if any:
try:
first_sub_key_name = _winreg.EnumKey(pg_inst_list_key, 0)
except EnvironmentError:
first_sub_key_name = None
if first_sub_key_name is not None:
pg_first_inst_key = _winreg.OpenKey(reg,
'SOFTWARE\\PostgreSQL\\Installations\\'
+ first_sub_key_name
)
try:
pg_inst_base_dir = _winreg.QueryValueEx(
pg_first_inst_key, 'Base Directory'
)[0]
finally:
_winreg.CloseKey(pg_first_inst_key)
finally:
_winreg.CloseKey(pg_inst_list_key)
if pg_inst_base_dir and os.path.exists(pg_inst_base_dir):
pg_config_path = os.path.join(pg_inst_base_dir, 'bin',
'pg_config.exe'
)
# Support unicode paths, if this version of Python provides the
# necessary infrastructure:
if hasattr(sys, 'getfilesystemencoding'):
pg_config_path = pg_config_path.encode(
sys.getfilesystemencoding()
)
return pg_config_path
# let's start with macro definitions (the ones not already in setup.cfg) # let's start with macro definitions (the ones not already in setup.cfg)
define_macros = [] define_macros = []
include_dirs = [] include_dirs = []
@ -183,7 +304,7 @@ define_macros.append(('PY_MINOR_VERSION', str(sys.version_info[1])))
# some macros related to python versions and features # some macros related to python versions and features
if sys.version_info[0] >= 2 and sys.version_info[1] >= 3: if sys.version_info[0] >= 2 and sys.version_info[1] >= 3:
define_macros.append(('HAVE_PYBOOL','1')) define_macros.append(('HAVE_PYBOOL','1'))
# gather information to build the extension module # gather information to build the extension module
ext = [] ; data_files = [] ext = [] ; data_files = []
@ -191,13 +312,12 @@ ext = [] ; data_files = []
sources = [ sources = [
'psycopgmodule.c', 'pqpath.c', 'typecast.c', 'psycopgmodule.c', 'pqpath.c', 'typecast.c',
'microprotocols.c', 'microprotocols_proto.c', 'microprotocols.c', 'microprotocols_proto.c',
'connection_type.c', 'connection_int.c', 'cursor_type.c', 'cursor_int.c', 'connection_type.c', 'connection_int.c', 'cursor_type.c', 'cursor_int.c',
'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c', 'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c',
'adapter_asis.c', 'adapter_list.c'] 'adapter_asis.c', 'adapter_list.c']
from ConfigParser import ConfigParser parser = ConfigParser.ConfigParser()
parser = ConfigParser()
parser.read('setup.cfg') parser.read('setup.cfg')
# Choose if to use Decimal type # Choose if to use Decimal type
@ -230,7 +350,7 @@ if os.path.exists(os.path.join(get_python_inc(plat_specific=1),"datetime.h")):
sources.append('adapter_datetime.c') sources.append('adapter_datetime.c')
have_pydatetime = True have_pydatetime = True
version_flags.append('dt') version_flags.append('dt')
# now decide which package will be the default for date/time typecasts # now decide which package will be the default for date/time typecasts
if have_pydatetime and use_pydatetime \ if have_pydatetime and use_pydatetime \
or have_pydatetime and not have_mxdatetime: or have_pydatetime and not have_mxdatetime:
@ -258,8 +378,8 @@ if version_flags:
PSYCOPG_VERSION_EX = PSYCOPG_VERSION + " (%s)" % ' '.join(version_flags) PSYCOPG_VERSION_EX = PSYCOPG_VERSION + " (%s)" % ' '.join(version_flags)
else: else:
PSYCOPG_VERSION_EX = PSYCOPG_VERSION PSYCOPG_VERSION_EX = PSYCOPG_VERSION
if sys.platform != 'win32': if not PLATFORM_IS_WINDOWS:
define_macros.append(('PSYCOPG_VERSION', '"'+PSYCOPG_VERSION_EX+'"')) define_macros.append(('PSYCOPG_VERSION', '"'+PSYCOPG_VERSION_EX+'"'))
else: else:
define_macros.append(('PSYCOPG_VERSION', '\\"'+PSYCOPG_VERSION_EX+'\\"')) define_macros.append(('PSYCOPG_VERSION', '\\"'+PSYCOPG_VERSION_EX+'\\"'))