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

154
setup.py
View File

@ -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
@ -97,35 +101,76 @@ class psycopg_build_ext(build_ext):
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")
@ -172,6 +217,82 @@ class psycopg_build_ext(build_ext):
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 = []
@ -196,8 +317,7 @@ sources = [
'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
@ -259,7 +379,7 @@ if 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+'\\"'))