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>
* 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__)
#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>
#define pthread_mutex_t HANDLE
#define pthread_condvar_t HANDLE
@ -102,7 +113,7 @@ static struct tm *localtime_r(time_t *t, struct tm *tm)
#define inline
#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? */
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;
char *d, *c;
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,
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);
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 */
if (c[0] == '%' && c[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
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 has refcnt inc'ed by 1 here */

View File

@ -1,5 +1,5 @@
[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_DISPLAY_SIZE enable display size calculation (a little slower)
# HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4

200
setup.py
View File

@ -13,13 +13,13 @@
# for more details.
"""Python-PostgreSQL Database Adapter
psycopg is a PostgreSQL database adapter for the Python programming
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
candies. Like the original, psycopg 2 was written with the aim of being
very small and fast, and stable as a rock.
psycopg is different from the other database adapter because it was
designed for heavily multi-threaded applications that create and destroy
lots of cursors and make a conspicuous number of concurrent INSERTs or
@ -44,8 +44,10 @@ Operating System :: Unix
"""
import os
import os.path
import sys
import popen2
import ConfigParser
from distutils.core import setup, Extension
from distutils.errors import DistutilsFileError
from distutils.command.build_ext import build_ext
@ -55,6 +57,8 @@ from distutils.ccompiler import get_default_compiler
PSYCOPG_VERSION = '2.0.6b1'
version_flags = []
PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win')
# to work around older distutil limitations
if sys.version < '2.2.3':
from distutils.dist import DistributionMetadata
@ -67,86 +71,127 @@ def get_pg_config(kind, pg_config="pg_config"):
if not r:
raise Warning(p[2].readline())
return r
class psycopg_build_ext(build_ext):
"""Conditionally complement the setup.cfg options file.
This class configures the include_dirs, libray_dirs, libraries
options as required by the system. Most of the configuration happens
in finalize_options() method.
If you want to set up the build step for a peculiar platform, add a
method finalize_PLAT(), where PLAT matches your sys.platform.
"""
user_options = build_ext.user_options[:]
user_options.extend([
('use-pydatetime', None,
"Use Python datatime objects for date and time representation."),
"Use Python datatime objects for date and time representation."),
('pg-config=', None,
"The name of the pg_config binary and/or full path to find it"),
('use-decimal', None,
"Use Decimal type even on Python 2.3 if the module is provided."),
])
boolean_options = build_ext.boolean_options[:]
boolean_options.extend(('use-pydatetime', 'use-decimal'))
DEFAULT_PG_CONFIG = "pg_config"
def initialize_options(self):
build_ext.initialize_options(self)
self.use_pg_dll = 1
self.pgdir = None
self.pg_config = self.DEFAULT_PG_CONFIG
self.mx_include_dir = None
self.pg_config = self.autodetect_pg_config_path()
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
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):
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):
"""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("advapi32")
if self.get_compiler() == "msvc":
if compiler_is_msvc:
# MSVC requires an explicit "libpq"
self.libraries.remove("pq")
self.libraries.append("libpq")
self.libraries.append("shfolder")
for path in self.library_dirs:
if os.path.isfile(os.path.join(path, "ms", "libpq.lib")):
self.library_dirs.append(os.path.join(path, "ms"))
for path in self.library_dirs:
if os.path.isfile(os.path.join(path, "ms", "libpq.lib")):
self.library_dirs.append(os.path.join(path, "ms"))
break
def finalize_darwin(self):
"""Finalize build system configuration on darwin platform."""
self.libraries.append('ssl')
self.libraries.append('crypto')
def finalize_options(self):
"""Complete the build system configuation."""
build_ext.finalize_options(self)
self.include_dirs.append(".")
self.include_dirs.append(".")
self.libraries.append("pq")
try:
self.library_dirs.append(self.get_pg_config("libdir"))
self.include_dirs.append(self.get_pg_config("includedir"))
@ -168,10 +213,86 @@ class psycopg_build_ext(build_ext):
else:
sys.stderr.write("Error: %s" % str(w))
sys.exit(1)
if hasattr(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)
define_macros = []
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
if sys.version_info[0] >= 2 and sys.version_info[1] >= 3:
define_macros.append(('HAVE_PYBOOL','1'))
# gather information to build the extension module
ext = [] ; data_files = []
@ -191,13 +312,12 @@ ext = [] ; data_files = []
sources = [
'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',
'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c',
'adapter_asis.c', 'adapter_list.c']
from ConfigParser import ConfigParser
parser = ConfigParser()
parser = ConfigParser.ConfigParser()
parser.read('setup.cfg')
# 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')
have_pydatetime = True
version_flags.append('dt')
# now decide which package will be the default for date/time typecasts
if have_pydatetime and use_pydatetime \
or have_pydatetime and not have_mxdatetime:
@ -258,8 +378,8 @@ if version_flags:
PSYCOPG_VERSION_EX = PSYCOPG_VERSION + " (%s)" % ' '.join(version_flags)
else:
PSYCOPG_VERSION_EX = PSYCOPG_VERSION
if sys.platform != 'win32':
if not PLATFORM_IS_WINDOWS:
define_macros.append(('PSYCOPG_VERSION', '"'+PSYCOPG_VERSION_EX+'"'))
else:
define_macros.append(('PSYCOPG_VERSION', '\\"'+PSYCOPG_VERSION_EX+'\\"'))