Initial psycopg 2 import after SVN crash.

This commit is contained in:
Federico Di Gregorio 2004-10-19 03:17:12 +00:00
commit c904d97f69
89 changed files with 12644 additions and 0 deletions

8
AUTHORS Normal file
View File

@ -0,0 +1,8 @@
Main authors:
Federico Di Gregorio <fog@debian.org>
For the win32 port:
Jason Erickson <jerickso@indian.com> (most of his changes are still in 2.0)
Additional Help:

362
ChangeLog Normal file
View File

@ -0,0 +1,362 @@
2004-10-14 Federico Di Gregorio <fog@debian.org>
* psycopg/cursor_type.c (_psyco_curs_buildrow_fill): now we use
PySequence_SetItem to avoid problems with containers created from
cursor's .tuple_factory attribute.
* lib/extras.py (DictCursor.execute): fixed stupid bug with cursor
setting self.tuplefactory instead of self.tuple_factory.
2004-10-02 Federico Di Gregorio <fog@debian.org>
* Release 1.99.10.
* psycopg/cursor_type.c (_psyco_curs_buildrow_*): unified normal
and factory code into the _psyco_curs_buildrow_fill function; no
more memory leaks here.
* psycopg/config.h (round): added check for __FreeBSD__ (that
should be defined when compiling with gcc, I hope.)
* setup.py: removed a lot of code now in setup.cfg.
2004-09-24 Federico Di Gregorio <fog@debian.org>
* psycopg/cursor_type.c (cursor_dealloc): fixed small memory leak
due to missing disposal of self->pgres.
2004-9-14 Federico Di Gregorio <fog@initd.org>
* examples/dialtone.py: Added adapt() example by Valentino
Volonghi.
2004-09-14 Federico Di Gregorio <fog@debian.org>
* psycopg/microprotocols.c (microprotocols_adapt): lots of changes
to the microprotocols layer (it is not micro anymore);
implementing almost all the PEP 246. The adapter registry is now
indexed by (type, protocol) and not by type alone.
2004-09-13 Federico Di Gregorio <fog@debian.org>
* psycopg/cursor_type.c (_mogrify): and qattr is gone.
2004-09-05 Federico Di Gregorio <fog@debian.org>
* Release 1.99.9 (or, the "twisting by the pool" release).
* psycopg/pqpath.c (_pq_fetch_tuples): changed to "static void"
instead of "static int", no ways for this function to fail.
2004-09-04 Federico Di Gregorio <fog@debian.org>
* psycopg/pqpath.c (_pq_fetch_tuples): ported rowcount fix from
1.1.15.
* ZPsycopgDA/*: ZPsycopgDA back in action, using the new pooling
code.
2004-08-29 Federico Di Gregorio <fog@debian.org>
* psycopg/typecast_basic.c (typecast_DECIMAL_cast): added DECIMAL
typecaster; it even works :).
* scripts/buildtypes.py (basic_types): added DECIMAL typecaster
for the NUMERIC oid.
* examples/threads.py: updated threads example to use pooling code.
* lib/pool.py: added very simple and thread-safe connection
pooling class.
* psycopg/cursor_type.c (psyco_curs_fetchmany): fixed problem with
.fetchall() and .fetchmany() returning None instead of [] on empty
result sets.
* Release 1.99.8.
2004-08-28 Federico Di Gregorio <fog@debian.org>
* psycopg/cursor_type.c (psyco_curs_execute): added processing of
unicode queries.
* examples/encoding.py: much better encoding example, also using
the new UNICODE typecaster.
* psycopg/typecast_basic.c (typecast_UNICODE_cast): added UNICODE
typecaster.
* lib/extensions.py: the encodings dictionary is not available by
default but can be accessed from the psycopg.extensions module.
* psycopg/adapter_qstring.h: remove encoding information from
qstring adapter and moved it into psycopg module.
2004-08-26 Federico Di Gregorio <fog@debian.org>
* psycopg/cursor_type.c (_psyco_curs_prefetch): added check for
asynchronous fetch by wrong cursor.
* psycopg/pqpath.c (pq_fetch): fixed backend status message (bug
reported by Daniele Varrazzo.)
2004-07-29 Federico Di Gregorio <fog@debian.org>
* psycopg/typecast_basic.c (typecast_BINARY_cast): reverted to
using strings instead of buffers when converting postgresql binary
objects (should *temporarily* fix corruption bug reported on
win32.)
2004-07-21 Federico Di Gregorio <fog@debian.org>
* psycopg/cursor_type.c: removed __iter__ and next methods from
object methods and moved them where they do belong (tp_iter and
tp_iternext.) Bug reported by Daniele Varrazzo (again!)
2004-07-19 Federico Di Gregorio <fog@debian.org>
* psycopg/typecast_datetime.c (typecast_PYINTERVAL_cast): replaced
round() with micro() when rounding seconds (fixes bugs reported by
Daniele Varrazzo.)
2004-07-16 Federico Di Gregorio <fog@debian.org>
* psycopg/pqpath.c (pq_set_critical): allow for a custom message
insted of the one from PQerrorMessage.
(pq_resolve_critical): added argument to specify if connection is
to be closed (used to not close it during COPY FROM/TO criticals.)
* psycopg/cursor_type.c (psyco_curs_fileno, psyco_curs_isready):
added extension methods related to async queries.
2004-07-15 Federico Di Gregorio <fog@debian.org>
* Release 1.99.7.
* examples/tz.py: added example about time zones.
* psycopg/typecast_datetime.c (typecast_PYDATETIME_cast): create
FixedOffsetTimezone for postgresql "timestamp with time zone"
types.
* lib/tz.py: added (even more than) needed tzinfo classes.
* psycopg/typecast.c (typecast_call): changed typecast call code
to take the additional cursor parameter, needed for
cursor-dependent type casting (tzinfo & friends.)
* psycopg/cursor_type.c (_psyco_curs_buildrow_with_factory): added
use of tuple factories to fetcXXX methods.
* lib/extras.py: little extra goodies for psycopg.
2004-07-14 Federico Di Gregorio <fog@debian.org>
* Release 1.99.6.
* psycopg/connection_type.c: added .dsn attribute to connection
objects.
* psycopg/cursor_type.c (psyco_curs_mogrify): added .mogrify()
method.
* psycopg/adapter_qstring.c: copy the connection encoding only if
wrapped object is unicode and added table of encodings.
2004-07-13 Federico Di Gregorio <fog@debian.org>
* psycopg/cursor_type.c (_mogrify): moved Dprintf statement to
avoid dereferencing empty pointer (from 1.1.x)
(psyco_curs_execute): now we save the query in self->query instead
of freeing the memory ASAP.
(cursorObject_members): and we finally export the saved query
through the cursor members interface. that's all folks.
* lib/extensions.py: added extensions module to clearly separate
psycopg own extensions from DBAPI-2.0
2004-07-10 Federico Di Gregorio <fog@debian.org>
* psycopg/typecast_datetime.c: ported interval fix from 1.1.x.
2004-05-16 Federico Di Gregorio <fog@debian.org>
* psycopg/typecast_datetime.c (typecast_*_cast): fixed Value error
when seconds > 59 by setting minutes += 1 and seconds -= 60
(reported by Marcel Gsteiger.)
2004-04-24 Federico Di Gregorio <fog@debian.org>
* ported time interval patch by Ross Cohen from 1.1.12.
2004-04-19 Federico Di Gregorio <fog@debian.org>
* psycopg/typecast_datetime.c (typecast_PYDATE_cast): applied
patch from Jason Erickson: min and max taken from datetime.Date
type.
2004-04-18 Federico Di Gregorio <fog@debian.org>
* Applied changes from Jason Erickson to build on win32; see his
(slightly edited) entry below. (Still builds on Linux :)
* psycopg/*.c: removed inclusion of pthread.h from all files
except psycopg/config.h to build on win32 without faking the file.
2004-04-15 Jason Erickson <jerickso@stickpeople.com>
* setup.py: Various changes. The critical ones:
- Make an empty pthread.h file so all the code doing an
#include <pthread.h> will find something.
- Appended the winsock2 library and the PostgreSQL library to
the library path.
- Setup the include path.
- Have the PSYCOPG_VERSION macro be included with quotes.
* config.h: Added/Cleaned up Win32 includes, defines, stub functions.
* typecast.h: Removed ';' after PyObject_HEAD in the
typecastObject structure since Microsoft Visual Studio does not
like it.
2004-04-15 Federico Di Gregorio <fog@debian.org>
* Release 1.99.5 (bug-fixing and reorganization)
* setup.py et al.: moved psycopg to psycopg._psycopg to make
easier to provide high level python-only utilities (like the
promised pooling code). psycopg/__init__.py imports _psycopg and
make all the default DBAPI-2.0 stuff available.
2004-04-14 Federico Di Gregorio <fog@debian.org>
* psycopg/psycopgmodule.c (initpsycopg): wrapped initialization of
date/time adapters in #ifdefs to have psycopg compile without mx
or builtin datetime.
2004-04-10 Federico Di Gregorio <fog@debian.org>
* Release 1.99.4.
2004-04-09 Federico Di Gregorio <fog@debian.org>
* psycopg/typecast_builtins.c: changed DATE to not include
DATETIME types anymore.
* psycopg/adapter_datetime.c (pydatetime_str): switched from
strftime to isoformat to preserve fractional seconds.
2004-04-08 Federico Di Gregorio <fog@debian.org>
* psycopg/psycopgmodule.c (psyco_connect): ported sslmode
parameter from 1.1 branch.
* psycopg/adapter_datetime.*: added python built-in datetime
adapters. also added the datetime typecasters (still using mx as
default).
* psycopg/typecast.h: removed aliases, they now live in the right
typecast_xxx.c file.
2004-03-08 Federico Di Gregorio <fog@debian.org>
* Release 1.99.3 (alpha 4).
* examples/lastrowid.py: and the .lastrowid example is in.
* psycopg/cursor_type.c (_mogrify): added call to .prepare()
method in both dict and sequence path.
* psycopg/connection_int.c (conn_set_client_encoding): added
encoding-change code.
* psycopg/adapter_qstring.c (qstring_quote): added hard-coded
support for utf8 and latin1 encodings.
2004-03-01 Federico Di Gregorio <fog@debian.org>
* psycopg/connection_int.c (conn_close): does not use libpq
functions on NULL pgconn (this can happen when conn_close is
called after a failed PQconnect.)
2004-02-29 Federico Di Gregorio <fog@debian.org>
* Release 1.99.2 (alpha 3).
* psycopg/cursor_type.c: added .rownumber and .connection
attributes. Also added .scroll(), .next() and .__iter__() methods
(see DBAPI2-.0 extensions on PEP.)
* psycopg/connection_type.c (psyco_conn_set_isolation_level):
added connection method .set_isolation_level(). Also added all
error objects to the connection (see DBAPI2-.0 extensions on PEP.)
* psycopg/connection_int.c (conn_switch_isolation_level): added
isolation level switching code.
* setup.py: removed all references to PSYCOPG_NEWSTYLE: support
for python < 2.2 has been dropped.
* typecast_basic.c (typecast_BINARY_cast): now binary objects are
returned as true buffers.
* adapter_binary.*: added adapter for buffers and binary (bytea)
objects.
* Release 1.99.1 (alpha 2).
* adapter_mxdatetime.*: added adapters for all mx.DateTime types.
2004-02-28 Federico Di Gregorio <fog@debian.org>
* cursor_type.c (_mogrify): complete rework of the mogrification
code to use the microprotocols_adapt function.
* typecast_basic.c (typecast_BOOLEAN_cast): we now return real
Py_True and Py_False values.
* microprotocols.h: added very simple microprotocols
implementation to allow for python->postgresql types registry.
2004-01-05 Federico Di Gregorio <fog@debian.org>
* connection_int.c (conn_commit/conn_rollback): added code to
commit/rollback and connection methods.
2004-01-04 Federico Di Gregorio <fog@debian.org>
* cursor_type.c (psyco_curs_fetchone): added fetchone method.
2004-01-03 Federico Di Gregorio <fog@debian.org>
* added (empty) INSTALL file.
* cursor_type.c (cursor_dealloc): added qattr for custom object
quoting using a callable attribute.
(_mogrify): ported new, fixed mogrification code from 1.1.12.
2003-08-01 Federico Di Gregorio <fog@debian.org>
* cursor_type.c (_mogrify_sequence): added sequence mogrification,
can be done better, on the dict model.
2003-07-28 Federico Di Gregorio <fog@debian.org>
* typeobj_qstring.c: added quoted strings (can use both own code,
like psycopg 1.x or PQescapeString from lipq.)
2003-07-21 Federico Di Gregorio <fog@debian.org>
* connection_type.c (psyco_conn_close): added .close()
method. wow.
* cursor_*.c: added basic cursor interface (new-style.)
2003-07-20 Federico Di Gregorio <fog@debian.org>
* psycopg/*: beginning of new source layout. if you think this
changelog is somewhat empty, you're right. look at
doc/ChangeLog-1.x for psycopg 1.x changelog just before the
branch.

18
INSTALL Normal file
View File

@ -0,0 +1,18 @@
Compiling and installing psycopg
********************************
While psycopg 1.x used autoconf for its build process psycopg 2 switched to
the more pythoning setup.py. Currently both psycopg's author and distutils
have some limitations so the file setup.cfg is almost unused and most build
options are hidden in setup.py. Before building psycopg look at the very
first lines of setup.py and change any settings to follow your system (or
taste); then:
python setup.py build
to build in the local directory; and:
python setup.py install
to install system-wide.

8
MANIFEST.in Normal file
View File

@ -0,0 +1,8 @@
recursive-include psycopg *.c *.h
recursive-include lib *.py
recursive-include ZPsycopgDA *.py *.gif *.dtml
recursive-include examples *.py somehackers.jpg whereareyou.jpg
#recursive-include test *.py
recursive-include doc TODO HACKING SUCCESS ChangeLog-1.x
include scripts/maketypes.sh scripts/buildtypes.py
include AUTHORS README INSTALL ChangeLog setup.py setup.cfg

155
NEWS Normal file
View File

@ -0,0 +1,155 @@
What's new in psycopg 1.99.10
-----------------------------
* The adapt() function now fully supports the adaptation protocol
described in PEP 246. Note that the adapters registry now is indexed
by (type, protocol) and not by type alone. Change your adapters
accordingly.
* More configuration options moved from setup.py to setup.cfg.
* Fixed two memory leaks: one in cursor deallocation and one in row
fetching (.fetchXXX() methods.)
What's new in psycopg 1.99.9
----------------------------
* Added simple pooling code (psycopg.pool module); see the reworked
examples/threads.py for example code.
* Added DECIMAL typecaster to convert postgresql DECIMAL and NUMERIC
types (i.e, all types with an OID of NUMERICOID.) Note that the
DECIMAL typecaster does not set scale and precision on the created
objects but uses Python defaults.
* ZPsycopgDA back in and working using the new pooling code.
* Isn't that enough? :)
What's new in psycopg 1.99.8
----------------------------
* added support for UNICODE queries.
* added UNICODE typecaster; to activate it just do:
psycopg.extensions.register_type(psycopg.extensions.UNICODE)
Note that the UNICODE typecaster override the STRING one, so it is
not activated by default.
* cursors now really support the iterator protocol.
* solved the rounding errors in time conversions.
* now cursors support .fileno() and .isready() methods, to be used in
select() calls.
* .copy_from() and .copy_in() methods are back in (still using the old
protocol, will be updated to use new one in next releasae.)
* fixed memory corruption bug reported on win32 platform.
What's new in psycopg 1.99.7
----------------------------
* added support for tuple factories in cursor objects (removed factory
argument in favor of a .tuple_factory attribute on the cursor object);
see the new module psycopg.extras for a cursor (DictCursor) that
return rows as objects that support indexing both by position and
column name.
* added support for tzinfo objects in datetime.timestamp objects: the
PostgreSQL type "timestamp with time zone" is converted to
datetime.timestamp with a FixedOffsetTimezone initialized as necessary.
What's new in psycopg 1.99.6
----------------------------
* sslmode parameter from 1.1.x
* various datetime conversion improvements.
* now psycopg should compile without mx or without native datetime
(not both, obviously.)
* included various win32/MSVC fixes (pthread.h changes, winsock2
library, include path in setup.py, etc.)
* ported interval fixes from 1.1.14/1.1.15.
* the last query executed by a cursor is now available in the
.query attribute.
* conversion of unicode strings to backend encoding now uses a table
(that still need to be filled.)
* cursors now have a .mogrify() method that return the query string
instead of executing it.
* connection objects now have a .dsn read-only attribute that holds the
connection string.
* moved psycopg C module to _psycopg and made psycopg a python module:
this allows for a neat separation of DBAPI-2.0 functionality and psycopg
extensions; the psycopg namespace will be also used to provide
python-only extensions (like the pooling code, some ZPsycopgDA support
functions and the like.)
What's new in psycopg 1.99.3
----------------------------
* added support for python 2.3 datetime types (both ways) and made datetime
the default set of typecasters when available.
* added example: dt.py.
What's new in psycopg 1.99.3
----------------------------
* initial working support for unicode bound variables: UTF-8 and latin-1
backend encodings are natively supported (and the encoding.py example even
works!)
* added .set_client_encoding() method on the connection object.
* added examples: encoding.py, binary.py, lastrowid.py.
What's new in psycopg 1.99.2
----------------------------
* better typecasting:
- DateTimeDelta used for postgresql TIME (merge from 1.1)
- BYTEA now is converted to a real buffer object, not to a string
* buffer objects are now adapted into Binary objects automatically.
* ported scroll method from 1.1 (DBAPI-2.0 extension for cursors)
* initial support for some DBAPI-2.0 extensions:
- .rownumber attribute for cursors
- .connection attribute for cursors
- .next() and .__iter__() methods to have cursors support the iterator
protocol
- all exception objects are exported to the connection object
What's new in psycopg 1.99.1
----------------------------
* implemented microprotocols to adapt arbitrary types to the interface used by
psycopg to bind variables in execute;
* moved qstring, pboolean and mxdatetime to the new adapter layout (binary is
still missing; python 2.3 datetime needs to be written).
What's new in psycopg 1.99.0
----------------------------
* reorganized the whole source tree;
* async core is in place;
* splitted QuotedString objects from mx stuff;
* dropped autotools and moved to pythonic setup.py (needs work.)

45
README Normal file
View File

@ -0,0 +1,45 @@
psycopg - 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
UPDATEs. psycopg 2 also provide full asycronous operations for the really
brave programmer.
There are confirmed reports of psycopg 1.x compiling and running on Linux
and FreeBSD on i386, Solaris, MacOS X and win32 architectures. psycopg 2
does not introduce build-wise incompatible changes so it should be able to
compile on all architectures just as its predecessor did.
Now go read the INSTALL file. More information about psycopg extensions to
the DBAPI-2.0 is available in the files located in the doc/ direcory.
Licence
-------
psycopg is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. See file COPYING for details.
As a special exception, specific permission is granted for the GPLed code in
this distribition to be linked to OpenSSL and PostgreSQL libpq without
invoking GPL clause 2(b).
If you prefer you can use the Zope Database Adapter ZPsycopgDA (i.e., every
file inside the ZPsycopgDA directory) under the ZPL license as published on
the Zope web site, http://www.zope.org/Resources/ZPL. The ZPL is perfectly
compatible with the GPL
psycopg is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.

202
ZPsycopgDA/DA.py Normal file
View File

@ -0,0 +1,202 @@
# ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection
#
# Copyright (C) 2004 Federico Di Gregorio <fog@initd.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# Or, at your option this program (ZPsycopgDA) can be distributed under the
# Zope Public License (ZPL) Version 1.0, as published on the Zope web site,
# http://www.zope.org/Resources/ZPL.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.
#
# See the LICENSE file for details.
ALLOWED_PSYCOPG_VERSIONS = ('1.99.9',)
import sys
import db
import DABase
import Shared.DC.ZRDB.Connection
from db import DB
from Globals import DTMLFile
from Globals import HTMLFile
from ImageFile import ImageFile
from ExtensionClass import Base
from DateTime import DateTime
# import psycopg and functions/singletons needed for date/time conversions
import psycopg
from psycopg import DATETIME
from psycopg.extensions import TIME, DATE, INTERVAL
from psycopg.extensions import new_type, register_type
# add a new connection to a folder
manage_addZPsycopgConnectionForm = DTMLFile('dtml/add',globals())
def manage_addZPsycopgConnection(self, id, title, connection_string,
zdatetime=None, tilevel=2,
check=None, REQUEST=None):
"""Add a DB connection to a folder."""
self._setObject(id, Connection(id, title, connection_string,
zdatetime, check, tilevel))
if REQUEST is not None: return self.manage_main(self, REQUEST)
# the connection object
class Connection(DABase.Connection):
"""ZPsycopg Connection."""
id = 'Psycopg_database_connection'
database_type = 'Psycopg'
meta_type = title = 'Z Psycopg Database Connection'
icon = 'misc_/ZPsycopg/conn'
def __init__(self, id, title, connection_string,
zdatetime, check=None, tilevel=2, encoding=''):
self.zdatetime = zdatetime
self.id = str(id)
self.edit(title, connection_string, zdatetime,
check=check, tilevel=tilevel, encoding=encoding)
def factory(self):
return DB
def table_info(self):
return self._v_database_connection.table_info()
def edit(self, title, connection_string,
zdatetime, check=None, tilevel=2, encoding=''):
self.title = title
self.connection_string = connection_string
self.zdatetime = zdatetime
self.tilevel = tilevel
self.encoding = encoding
self.set_type_casts()
if check: self.connect(self.connection_string)
manage_properties = DTMLFile('dtml/edit', globals())
def manage_edit(self, title, connection_string,
zdatetime=None, check=None, tilevel=2, encoding='UTF-8',
REQUEST=None):
"""Edit the DB connection."""
self.edit(title, connection_string, zdatetime,
check=check, tilevel=tilevel, encoding=encoding)
if REQUEST is not None:
msg = "Connection edited."
return self.manage_main(self,REQUEST,manage_tabs_message=msg)
def connect(self, s):
try:
self._v_database_connection.close()
except:
pass
# check psycopg version and raise exception if does not match
if psycopg.__version__ not in ALLOWED_PSYCOPG_VERSIONS:
raise ImportError("psycopg version mismatch (imported %s)" +
psycopg.__version__)
self.set_type_casts()
self._v_connected = ''
dbf = self.factory()
# TODO: let the psycopg exception propagate, or not?
self._v_database_connection = dbf(
self.connection_string, self.tilevel, self.encoding)
self._v_database_connection.open()
self._v_connected = DateTime()
return self
def set_type_casts(self):
# note that in both cases order *is* important
if self.zdatetime:
# use zope internal datetime routines
register_type(ZDATETIME)
register_type(ZDATE)
register_type(ZTIME)
register_type(ZINTERVAL)
else:
# use the standard
register_type(DATETIME)
register_type(DATE)
register_type(TIME)
register_type(INTERVAL)
# database connection registration data
classes = (Connection,)
meta_types = ({'name':'Z Psycopg Database Connection',
'action':'manage_addZPsycopgConnectionForm'},)
folder_methods = {
'manage_addZPsycopgConnection': manage_addZPsycopgConnection,
'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm}
__ac_permissions__ = (
('Add Z Psycopg Database Connections',
('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),)
# add icons
misc_={'conn': ImageFile('Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif')}
for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin',
'int', 'float', 'date', 'time', 'datetime'):
misc_[icon] = ImageFile('icons/%s.gif' % icon, globals())
# zope-specific psycopg typecasters
# convert an ISO timestamp string from postgres to a Zope DateTime object
def _cast_DateTime(str):
if str:
# this will split us into [date, time, GMT/AM/PM(if there)]
dt = split(str, ' ')
if len(dt) > 1:
# we now should split out any timezone info
dt[1] = split(dt[1], '-')[0]
dt[1] = split(dt[1], '+')[0]
return DateTime(join(dt[:2], ' '))
else:
return DateTime(dt[0])
# convert an ISO date string from postgres to a Zope DateTime object
def _cast_Date(str):
if str:
return DateTime(str)
# Convert a time string from postgres to a Zope DateTime object.
# NOTE: we set the day as today before feeding to DateTime so
# that it has the same DST settings.
def _cast_Time(str):
if str:
return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
time.localtime(time.time())[:3]+
time.strptime(str[:8], "%H:%M:%S")[3:]))
# TODO: DateTime does not support intervals: what's the best we can do?
def _cast_Interval(str):
return str
ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime)
ZINTERVAL = new_type((1186,), "ZINTERVAL", _cast_Interval)
ZDATE = new_type((1082,), "ZDATE", _cast_Date)
ZTIME = new_type((1083,), "ZTIME", _cast_Time)

67
ZPsycopgDA/DABase.py Normal file
View File

@ -0,0 +1,67 @@
# ZPsycopgDA/DABase.py - ZPsycopgDA Zope product: Database inspection
#
# Copyright (C) 2004 Federico Di Gregorio <fog@initd.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# Or, at your option this program (ZPsycopgDA) can be distributed under the
# Zope Public License (ZPL) Version 1.0, as published on the Zope web site,
# http://www.zope.org/Resources/ZPL.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.
#
# See the LICENSE file for details.
import sys
import Shared.DC.ZRDB.Connection
from db import DB
from Globals import HTMLFile
from ImageFile import ImageFile
from ExtensionClass import Base
from DateTime import DateTime
# import psycopg and functions/singletons needed for date/time conversions
import psycopg
from psycopg.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN
from psycopg import NUMBER, STRING, ROWID, DATETIME
class Connection(Shared.DC.ZRDB.Connection.Connection):
_isAnSQLConnection = 1
info = None
#manage_options = Shared.DC.ZRDB.Connection.Connection.manage_options + (
# {'label': 'Browse', 'action':'manage_browse'},)
#manage_tables = HTMLFile('tables', globals())
#manage_browse = HTMLFile('browse',globals())
def __getitem__(self, name):
if name == 'tableNamed':
if not hasattr(self, '_v_tables'): self.tpValues()
return self._v_tables.__of__(self)
raise KeyError, name
## old stuff from ZPsycopgDA 1.1 (never implemented) ##
def manage_wizard(self, tables):
"Wizard of what? Oozing?"
def manage_join(self, tables, select_cols, join_cols, REQUEST=None):
"""Create an SQL join"""
def manage_insert(self, table, cols, REQUEST=None):
"""Create an SQL insert"""
def manage_update(self, table, keys, cols, REQUEST=None):
"""Create an SQL update"""

32
ZPsycopgDA/__init__.py Normal file
View File

@ -0,0 +1,32 @@
# ZPsycopgDA/__init__.py - ZPsycopgDA Zope product
#
# Copyright (C) 2004 Federico Di Gregorio <fog@initd.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# Or, at your option this program (ZPsycopgDA) can be distributed under the
# Zope Public License (ZPL) Version 1.0, as published on the Zope web site,
# http://www.zope.org/Resources/ZPL.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.
#
# See the LICENSE file for details.
__doc__ = "ZPsycopg Database Adalper Registration."
__version__ = '2.0'
import sys
import string
import DA
methods = DA.folder_methods
classes = DA.classes
meta_types = DA.meta_types
misc_ = DA.misc_
__ac_permissions__=DA.__ac_permissions__

209
ZPsycopgDA/db.py Normal file
View File

@ -0,0 +1,209 @@
# ZPsycopgDA/db.py - query execution
#
# Copyright (C) 2004 Federico Di Gregorio <fog@initd.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# Or, at your option this program (ZPsycopgDA) can be distributed under the
# Zope Public License (ZPL) Version 1.0, as published on the Zope web site,
# http://www.zope.org/Resources/ZPL.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.
#
# See the LICENSE file for details.
from Shared.DC.ZRDB.TM import TM
from Shared.DC.ZRDB import dbi_db
from ZODB.POSException import ConflictError
import time
import site
import pool
import psycopg
from psycopg.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN
from psycopg import NUMBER, STRING, ROWID, DATETIME
# the DB object, managing all the real query work
class DB(TM, dbi_db.DB):
_p_oid = _p_changed = _registered = None
def __init__(self, dsn, tilevel, enc='utf-8'):
self.dsn = dsn
self.tilevel = tilevel
self.encoding = enc
self.failures = 0
self.calls = 0
def getconn(self, create=True):
conn = pool.getconn(self.dsn)
conn.set_isolation_level(int(self.tilevel))
return conn
def putconn(self, close=False):
try:
conn = pool.getconn(self.dsn, False)
except AttributeError:
pass
pool.putconn(self.dsn, conn, close)
def getcursor(self):
conn = self.getconn()
return conn.cursor()
def _finish(self, *ignored):
try:
conn = self.getconn(False)
conn.commit()
self.putconn()
except AttributeError:
pass
def _abort(self, *ignored):
try:
conn = self.getconn(False)
conn.rollback()
self.putconn()
except AttributeError:
pass
def open(self):
# this will create a new pool for our DSN if not already existing,
# then get and immediately release a connection
self.getconn()
self.putconn()
def close(self):
# FIXME: if this connection is closed we flush all the pool associated
# with the current DSN; does this makes sense?
pool.flushpool(self.dsn)
def sortKey(self):
return 1
## tables and rows ##
def tables(self, rdb=0, _care=('TABLE', 'VIEW')):
self._register()
c = self.getcursor()
c.execute(
"SELECT t.tablename AS NAME, 'TABLE' AS TYPE "
" FROM pg_tables t WHERE tableowner <> 'postgres' "
"UNION SELECT v.viewname AS NAME, 'VIEW' AS TYPE "
" FROM pg_views v WHERE viewowner <> 'postgres' "
"UNION SELECT t.tablename AS NAME, 'SYSTEM_TABLE\' AS TYPE "
" FROM pg_tables t WHERE tableowner = 'postgres' "
"UNION SELECT v.viewname AS NAME, 'SYSTEM_TABLE' AS TYPE "
"FROM pg_views v WHERE viewowner = 'postgres'")
res = []
for name, typ in c.fetchall():
if typ in _care:
res.append({'TABLE_NAME': name, 'TABLE_TYPE': typ})
self.putconn()
return res
def columns(self, table_name):
self._register()
c = self.getcursor()
try:
r = c.execute('SELECT * FROM "%s" WHERE 1=0' % table_name)
except:
return ()
res = []
for name, type, width, ds, p, scale, null_ok in c.description:
if type == NUMBER:
if type == INTEGER:
type = INTEGER
elif type == FLOAT:
type = FLOAT
else: type = NUMBER
elif type == BOOLEAN:
type = BOOLEAN
elif type == ROWID:
type = ROWID
elif type == DATETIME:
type = DATETIME
else:
type = STRING
res.append({'Name': name,
'Type': type.name,
'Precision': 0,
'Scale': 0,
'Nullable': 0})
self.putconn()
return res
## query execution ##
def query(self, query_string, max_rows=None, query_data=None):
self._register()
self.calls = self.calls+1
desc = ()
res = []
nselects = 0
c = self.getcursor()
try:
for qs in [x for x in query_string.split('\0') if x]:
if type(qs) == unicode:
if self.encoding:
qs = qs.encode(self.encoding)
try:
if (query_data):
c.execute(qs, query_data)
else:
c.execute(qs)
except (psycopg.ProgrammingError, psycopg.IntegrityError), e:
if e.args[0].find("concurrent update") > -1:
raise ConflictError
raise e
if c.description is not None:
nselects += 1
if c.description != desc and nselects > 1:
raise psycopg.ProgrammingError(
'multiple selects in single query not allowed')
if max_rows:
res = c.fetchmany(max_rows)
else:
res = c.fetchall()
desc = c.description
self.failures = 0
except StandardError, err:
self._abort()
raise err
items = []
for name, typ, width, ds, p, scale, null_ok in desc:
if typ == NUMBER:
if typ == INTEGER or typ == LONGINTEGER: typs = 'i'
else: typs = 'n'
elif typ == BOOLEAN:
typs = 'n'
elif typ == ROWID:
typs = 'i'
elif typ == DATETIME:
typs = 'd'
else:
typs = 's'
items.append({
'name': name,
'type': typs,
'width': width,
'null': null_ok,
})
return items, res

96
ZPsycopgDA/dtml/add.dtml Normal file
View File

@ -0,0 +1,96 @@
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add Z Psycopg Database Connection',
help_product='ZPsycopgDA',
help_topic='ZPsycopgDA-Method-Add.stx'
)">
<p class="form-help">
A Zope Psycopg Database Connection is used to connect and execute
queries on a PostgreSQL database.
</p>
<p class="form-help">
In the form below <em>Connection String</em> (also called the Data Source Name
or DSN for short) is a string... (TODO: finish docs)
</p>
<form action="manage_addZPsycopgConnection" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40"
value="Psycopg_database_connection" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40"
value="Z Psycopg Database Connection"/>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connection string
</div>
</td>
<td align="left" valign="top">
<input type="text" name="connection_string" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connect immediately
</div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="check" value="YES" checked="YES" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Use Zope's internal DateTime
</div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="zdatetime" value="YES" checked="YES" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Transaction isolation level
</div>
</td>
<td align="left" valign="top">
<select name="tilevel:int">
<option value="1">Read committed</option>
<option value="2" selected="YES">Serializable</option>
</select>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit" name="submit" value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>

67
ZPsycopgDA/dtml/edit.dtml Normal file
View File

@ -0,0 +1,67 @@
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<form action="manage_edit" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40"
value="&dtml-title;"/>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connection string
</div>
</td>
<td align="left" valign="top">
<input type="text" name="connection_string" size="40"
value="&dtml-connection_string;" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Use Zope's internal DateTime
</div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="zdatetime" value="YES"
<dtml-if expr="zdatetime">checked="YES"</dtml-if> />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Transaction isolation level
</div>
</td>
<td align="left" valign="top">
<select name="tilevel:int">
<option value="1"
<dtml-if expr="tilevel==1">selected="YES"</dtml-if">>
Read committed</option>
<option value="2"
<dtml-if expr="tilevel==2">selected="YES"</dtml-if">>
Serializable</option>
</select>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Save Changes " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>

BIN
ZPsycopgDA/icons/bin.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

BIN
ZPsycopgDA/icons/date.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

BIN
ZPsycopgDA/icons/field.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

BIN
ZPsycopgDA/icons/float.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

BIN
ZPsycopgDA/icons/int.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

BIN
ZPsycopgDA/icons/stable.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

BIN
ZPsycopgDA/icons/table.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
ZPsycopgDA/icons/text.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

BIN
ZPsycopgDA/icons/time.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

BIN
ZPsycopgDA/icons/view.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

BIN
ZPsycopgDA/icons/what.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

51
ZPsycopgDA/pool.py Normal file
View File

@ -0,0 +1,51 @@
# ZPsycopgDA/pool.py - ZPsycopgDA Zope product: connection pooling
#
# Copyright (C) 2004 Federico Di Gregorio <fog@initd.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# Or, at your option this program (ZPsycopgDA) can be distributed under the
# Zope Public License (ZPL) Version 1.0, as published on the Zope web site,
# http://www.zope.org/Resources/ZPL.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.
#
# See the LICENSE file for details.
# all the connections are held in a pool of pools, directly accessible by the
# ZPsycopgDA code in db.py
import threading
import psycopg.pool
_connections_pool = {}
_connections_lock = threading.Lock()
def getpool(dsn, create=True):
_connections_lock.acquire()
try:
if not _connections_pool.has_key(dsn) and create:
_connections_pool[dsn] = \
psycopg.pool.ThreadedConnectionPool(4, 200, dsn)
finally:
_connections_lock.release()
return _connections_pool[dsn]
def flushpool(dsn):
_connections_lock.acquire()
try:
_connections_pool[dsn].closeall()
del _connections_pool[dsn]
finally:
_connections_lock.release()
def getconn(dsn, create=True):
return getpool(dsn, create=create).getconn()
def putconn(dsn, conn, close=False):
getpool(dsn).putconn(conn, close=close)

1744
doc/ChangeLog-1.x Normal file

File diff suppressed because it is too large Load Diff

43
doc/HACKING Normal file
View File

@ -0,0 +1,43 @@
General information
*******************
Some help to people wanting to hack on psycopg. First of all, note that
*every* function in the psycopg module source code is prefixed by one of the
following words:
psyco is used for function directly callable from python (i.e., functions
in the psycopg module itself.) the only notable exception is the
source code for the module itself, that uses "psyco" even for C-only
functions.
conn is used for functions related to connection objects.
curs is used for functions related to cursor objects.
typecast is used for typecasters and utility function related to
typecaster creation and registration.
Pythonic definition of types and functions available from python are defined
in *_type.c files. Internal functions, callable only from C are located in
*_int.c files and extensions to the DBAPI can be found in the *_ext.c files.
Patches
*******
If you submit a patch, please send a diff generated with the "-u" switch.
Also note that I don't like that much cosmetic changes (like renaming
already existing variables) and I will rewrap the patch to 78 columns
anyway, so it is much better if you do that beforehand.
The type system
***************
Simple types, like integers and strings, are converted to python base types
(the conversion functions are in typecast_base.c). Complex types are
converted to ad-hoc types, defined in the typeobj_*.{c,h} files. The
conversion function are in the other typecast_*.c files. typecast.c defines
the basic utility functions (available through the psycopg module) used when
defining new typecasters from C and python.

114
doc/SUCCESS Normal file
View File

@ -0,0 +1,114 @@
From: Jack Moffitt <jack@xiph.org>
To: Psycopg Mailing List <psycopg@lists.initd.org>
Subject: Re: [Psycopg] preparing for 1.0
Date: 22 Oct 2001 11:16:21 -0600
www.vorbis.com is serving from 5-10k pages per day with psycopg serving
data for most of that.
I plan to use it for several of our other sites, so that number will
increase.
I've never had a single problem (that wasn't my fault) besides those
segfaults, and those are now gone as well, and I've been using psycopg
since June (around 0.99.2?).
jack.
From: Yury Don <gercon@vpcit.ru>
To: Psycopg Mailing List <psycopg@lists.initd.org>
Subject: Re: [Psycopg] preparing for 1.0
Date: 23 Oct 2001 09:53:11 +0600
We use psycopg and psycopg zope adapter since fisrt public
release (it seems version 0.4). Now it works on 3 our sites and in intranet
applications. We had few problems, but all problems were quckly
solved. The strong side of psycopg is that it's code is well organized
and easy to understand. When I found a problem with non-ISO datestyle in first
version of psycopg, it took for me 15 or 20 minutes to learn code and
to solve the problem, even thouth my knowledge of c were poor.
BTW, segfault with dictfetchall on particular data set (see [Psycopg]
dictfetchXXX() problems) disappeared in 0.99.8pre2.
--
Best regards,
Yury Don
From: Tom Jenkins <tjenkins@devis.com>
To: Federico Di Gregorio <fog@debian.org>
Cc: Psycopg Mailing List <psycopg@lists.initd.org>
Subject: Re: [Psycopg] preparing for 1.0
Date: 23 Oct 2001 08:25:52 -0400
The US Govt Department of Labor's Office of Disability Employment
Policy's DisabilityDirect website is run on zope and zpsycopg.
From: Scott Leerssen <sleerssen@racemi.com>
To: Federico Di Gregorio <fog@debian.org>
Subject: Re: [Psycopg] preparing for 1.0
Date: 23 Oct 2001 09:56:10 -0400
Racemi's load management software infrastructure uses psycopg to handle
complex server allocation decisions, plus storage and access of
environmental conditions and accounting records for potentially
thousands of servers. Psycopg has, to this point, been the only
Python/PostGreSQL interface that could handle the scaling required for
our multithreaded applications.
Scott
From: Andre Schubert <andre.schubert@geyer.kabeljournal.de>
To: Federico Di Gregorio <fog@debian.org>
Cc: Psycopg Mailing List <psycopg@lists.initd.org>
Subject: Re: [Psycopg] preparing for 1.0
Date: 23 Oct 2001 11:46:07 +0200
i have changed the psycopg version to 0.99.8pre2 on all devel-machines
and all segfaults are gone. after my holiday i wil change to 0.99.8pre2
or 1.0 on our production-server.
this server contains several web-sites which are all connected to
postgres over ZPsycopgDA.
thanks as
From: Fred Wilson Horch <fhorch@ecoaccess.org>
To: <psycopg@lists.initd.org>
Subject: [Psycopg] Success story for psycopg
Date: 23 Oct 2001 10:59:17 -0400
Due to various quirks of PyGreSQL and PoPy, EcoAccess has been looking for
a reliable, fast and relatively bug-free Python-PostgreSQL interface for
our project.
Binary support in psycopg, along with the umlimited tuple size in
PostgreSQL 7.1, allowed us to quickly prototype a database-backed file
storage web application, which we're using for file sharing among our
staff and volunteers. Using a database backend instead of a file system
allows us to easily enrich the meta-information associated with each file
and simplifies our data handling routines.
We've been impressed by the responsiveness of the psycopg team to bug
reports and feature requests, and we're looking forward to using psycopg
as the Python interface for additional database-backed web applications.
Keep up the good work!
--
Fred Wilson Horch mailto:fhorch@ecoaccess.org
Executive Director, EcoAccess http://ecoaccess.org/
From: Damon Fasching <fasching@design.lbl.gov>
To: Michele Comitini <mcm@glisco.it>
Cc: fog@debian.org
Subject: Re: How does one create a database within Python using psycopg?
Date: 25 Feb 2002 17:39:41 -0800
[snip]
btw I checked out 4 different Python-PostgreSQL packages. psycopg is the
only one which built and imported w/o any trouble! (At least for me.)

33
doc/TODO Normal file
View File

@ -0,0 +1,33 @@
TODO list for psycopg 2 or later
********************************
Move items to the DONE section only after writing a test for the
implementation. Also add a note on how the item was resolved.
(Obviously I was joking about the test..)
* Find a better way to compile the type-casting code instead of including it
in typecast.c directy. (Including is not that bad, but the need to touch
psycopg/typecast.c every time is bad bad bad.)
* executemany() should _not_ take the async flag, remove it and force multiple
queries to be synchronous.
* Fix all the docstrings.
* Support the protocols API fully.
* Unify the common code in typecast_datetime.c and typecast_mxdatetime.c.
* Port typecasters to new-style classes.
* Write a complete postgresql<->python encodings table.
* Implement binary typecasters (should be easy, but it will take time.)
DONE
====
* Convert type-casters to new-style types in Python 2.2+.
* callproc() never worked, fix it or remove it and raise right exception.
[Removed callproc code, now an exception is raised.]

88
examples/binary.py Normal file
View File

@ -0,0 +1,88 @@
# binary.py - working with binary data
#
# Copyright (C) 2001-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## don't modify anything below tis line (except for experimenting)
import sys, psycopg
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
print "Encoding for this connection is", conn.encoding
curs = conn.cursor()
try:
curs.execute("CREATE TABLE test_binary (id int4, name text, img bytea)")
except:
conn.rollback()
curs.execute("DROP TABLE test_binary")
curs.execute("CREATE TABLE test_binary (id int4, name text, img bytea)")
conn.commit()
# first we try two inserts, one with an explicit Binary call and the other
# using a buffer on a file object.
data1 = {'id':1, 'name':'somehackers.jpg',
'img':psycopg.Binary(open('somehackers.jpg').read())}
data2 = {'id':2, 'name':'whereareyou.jpg',
'img':buffer(open('whereareyou.jpg').read())}
curs.execute("""INSERT INTO test_binary
VALUES (%(id)d, %(name)s, %(img)s)""", data1)
curs.execute("""INSERT INTO test_binary
VALUES (%(id)d, %(name)s, %(img)s)""", data2)
# now we try to extract the images as simple text strings
print "Extracting the images as strings..."
curs.execute("SELECT * FROM test_binary")
for row in curs.fetchall():
name, ext = row[1].split('.')
new_name = name + '_S.' + ext
print " writing %s to %s ..." % (name+'.'+ext, new_name),
open(new_name, 'wb').write(row[2])
print "done"
print " python type of image data is", type(row[2])
# extract exactly the same data but using a binary cursor
print "Extracting the images using a binary cursor:"
curs.execute("""DECLARE zot CURSOR FOR
SELECT img, name FROM test_binary FOR READ ONLY""")
curs.execute("""FETCH ALL FROM zot""")
for row in curs.fetchall():
name, ext = row[1].split('.')
new_name = name + '_B.' + ext
print " writing %s to %s ..." % (name+'.'+ext, new_name),
open(new_name, 'wb').write(row[0])
print "done"
print " python type of image data is", type(row[0])
# this rollback is requires because we can't drop a table with a binary cusor
# declared and still open
conn.rollback()
curs.execute("DROP TABLE test_binary")
conn.commit()
print "\nNow try to load the new images, to check it worked!"

64
examples/cursor.py Normal file
View File

@ -0,0 +1,64 @@
# cursor.py - how to subclass the cursor type
#
# Copyright (C) 2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## don't modify anything below this line (except for experimenting)
import sys
import psycopg
import psycopg.extensions
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dsn:", DSN
conn = psycopg.connect(DSN)
print "Encoding for this connection is", conn.encoding
class NoDataError(psycopg.ProgrammingError):
"""Exception that will be raised by our cursor."""
pass
class Cursor(psycopg.extensions.cursor):
"""A custom cursor."""
def fetchone(self):
"""Like fetchone but raise an exception if no data is available.
Note that to have .fetchmany() and .fetchall() to raise the same
exception we'll have to override them too; even if internally psycopg
uses the same function to fetch rows, the code path from Python is
different.
"""
d = psycopg.extensions.cursor.fetchone(self)
if d is None:
raise NoDataError("no more data")
return d
curs = conn.cursor(factory=Cursor)
curs.execute("SELECT 1 AS foo")
print "Result of fetchone():", curs.fetchone()
# now let's raise the exception
try:
curs.fetchone()
except NoDataError, err:
print "Exception caugth:", err
conn.rollback()

145
examples/dialtone.py Normal file
View File

@ -0,0 +1,145 @@
"""
This example/recipe has been contributed by Valentino Volonghi (dialtone)
Mapping arbitrary objects to a PostgreSQL database with psycopg2
- Problem
You need to store arbitrary objects in a PostgreSQL database without being
intrusive for your classes (don't want inheritance from an 'Item' or
'Persistent' object).
- Solution
"""
from datetime import datetime
import psycopg
from psycopg.extensions import adapters, adapt
try: sorted()
except NameError:
def sorted(seq):
seq.sort()
return seq
# Here is the adapter for every object that we may ever need to
# insert in the database. It receives the original object and does
# its job on that instance
class ObjectMapper(object):
def __init__(self, orig):
self.orig = orig
self.tmp = {}
self.items, self.fields = self._gatherState()
def _gatherState(self):
adaptee_name = self.orig.__class__.__name__
fields = sorted([(field, getattr(self.orig, field))
for field in persistent_fields[adaptee_name]])
items = []
for item, value in fields:
items.append(item)
return items, fields
def getTableName(self):
return self.orig.__class__.__name__
def getMappedValues(self):
tmp = []
for i in self.items:
tmp.append("%%(%s)s"%i)
return ", ".join(tmp)
def getValuesDict(self):
return dict(self.fields)
def getFields(self):
return self.items
def generateInsert(self):
qry = "INSERT INTO"
qry += " " + self.getTableName() + " ("
qry += ", ".join(self.getFields()) + ") VALUES ("
qry += self.getMappedValues() + ")"
return qry, self.getValuesDict()
# Here are the objects
class Album(object):
id = 0
def __init__(self):
self.creation_time = datetime.now()
self.album_id = self.id
Album.id = Album.id + 1
self.binary_data = buffer('12312312312121')
class Order(object):
id = 0
def __init__(self):
self.items = ['rice','chocolate']
self.price = 34
self.order_id = self.id
Order.id = Order.id + 1
adapters.update({Album: ObjectMapper, Order: ObjectMapper})
# Describe what is needed to save on each object
# This is actually just configuration, you can use xml with a parser if you
# like to have plenty of wasted CPU cycles ;P.
persistent_fields = {'Album': ['album_id', 'creation_time', 'binary_data'],
'Order': ['order_id', 'items', 'price']
}
print adapt(Album()).generateInsert()
print adapt(Album()).generateInsert()
print adapt(Album()).generateInsert()
print adapt(Order()).generateInsert()
print adapt(Order()).generateInsert()
print adapt(Order()).generateInsert()
"""
- Discussion
Psycopg 2 has a great new feature: adaptation. The big thing about
adaptation is that it enable the programmer to glue most of the
code out there without many difficulties.
This recipe tries to focus the attention on a way to generate SQL queries to
insert completely new objects inside a database. As you can see objects do
not know anything about the code that is handling them. We specify all the
fields that we need for each object through the persistent_fields dict.
The most important line of this recipe is this one:
adapters.update({Album: ObjectMapper, Order: ObjectMapper})
In this line we notify the system that when we call adapt with an Album instance
as an argument we want it to istantiate ObjectMapper passing the Album instance
as argument (self.orig in the ObjectMapper class).
adapters is just a python dict with a Key that represents the type
we need to adapt from and a value that is the adapter
which will adapt to the wanted interface.
The output is something like this (for each call to generateInsert):
('INSERT INTO Album (album_id, binary_data, creation_time) VALUES
(%(album_id)s, %(binary_data)s, %(creation_time)s)',
{'binary_data': <read-only buffer for 0x402de070, ...>,
'creation_time': datetime.datetime(2004, 9, 10, 20, 48, 29, 633728),
'album_id': 1}
)
This is a tuple of {SQL_QUERY, FILLING_DICT}, and all the quoting/converting
stuff (from python's datetime to postgres s and from python's buffer to
postgres' blob) is handled with the same adaptation process hunder the hood
by psycopg2.
At last, just notice that ObjectMapper is working for both Album and Order
instances without any glitches at all, and both classes could have easily been
coming from closed source libraries or C coded ones (which are not easily
modified), whereas a common pattern in todays ORMs or OODBs is to provide
a basic 'Persistent' object that already knows how to store itself in the
database.
"""

91
examples/dt.py Normal file
View File

@ -0,0 +1,91 @@
# datetime.py - example of using date and time types
#
# Copyright (C) 2001-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## don't modify anything below tis line (except for experimenting)
import sys, psycopg
import mx.DateTime
import datetime
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
curs = conn.cursor()
try:
curs.execute("""CREATE TABLE test_dt (k int4, d date, t time, dt timestamp,
z interval)""")
except:
conn.rollback()
curs.execute("DROP TABLE test_dt")
curs.execute("""CREATE TABLE test_dt (k int4,
d date, t time, dt timestamp,
z interval)""")
conn.commit()
# build and insert some data using mx.DateTime
mx1 = (
1,
mx.DateTime.Date(2004, 10, 19),
mx.DateTime.Time(0, 11, 17.015),
mx.DateTime.Timestamp(2004, 10, 19, 0, 11, 17.5),
mx.DateTime.DateTimeDelta(13, 15, 17, 59.9))
print "Inserting mx.DateTime values..."
curs.execute("INSERT INTO test_dt VALUES (%s, %s, %s, %s, %s)", mx1)
# build and insert some values using the datetime adapters
dt1 = (
2,
datetime.date(2004, 10, 19),
datetime.time(0, 11, 17, 15000),
datetime.datetime(2004, 10, 19, 0, 11, 17, 500000),
datetime.timedelta(13, 15*3600+17*60+59, 900000))
print "Inserting Python datetime values..."
curs.execute("INSERT INTO test_dt VALUES (%s, %s, %s, %s, %s)", dt1)
# now extract the row from database and print them
print "Extracting values inserted with mx.DateTime wrappers:"
curs.execute("SELECT d, t, dt, z FROM test_dt WHERE k = 1")
for n, x in zip(mx1[1:], curs.fetchone()):
try:
# this will work only is psycopg has been compiled with datetime
# as the default typecaster for date/time values
s = repr(n) + "\n -> " + repr(x) + "\n -> " + x.isoformat()
except:
s = repr(n) + "\n -> " + repr(x) + "\n -> " + str(x)
print s
print
print "Extracting values inserted with Python datetime wrappers:"
curs.execute("SELECT d, t, dt, z FROM test_dt WHERE k = 2")
for n, x in zip(dt1[1:], curs.fetchone()):
try:
# this will work only is psycopg has been compiled with datetime
# as the default typecaster for date/time values
s = repr(n) + "\n -> " + repr(x) + "\n -> " + x.isoformat()
except:
s = repr(n) + "\n -> " + repr(x) + "\n -> " + str(x)
print s
print
curs.execute("DROP TABLE test_dt")
conn.commit()

102
examples/encoding.py Normal file
View File

@ -0,0 +1,102 @@
# encoding.py - how to change client encoding (and test it works)
# -*- encoding: latin-1 -*-
#
# Copyright (C) 2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## don't modify anything below this line (except for experimenting)
import sys, psycopg
import psycopg.extensions
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
print "Initial encoding for this connection is", conn.encoding
print "\n** This example is supposed to be run in a UNICODE terminal! **\n"
print "Available encodings:"
for a, b in psycopg.extensions.encodings.items():
print " ", a, "<->", b
print "Using STRING typecaster"
print "Setting backend encoding to LATIN1 and executing queries:"
conn.set_client_encoding('LATIN1')
curs = conn.cursor()
curs.execute("SELECT %s::TEXT AS foo", ('àèìòù',))
x = curs.fetchone()[0]
print " ->", unicode(x, 'latin-1').encode('utf-8'), type(x)
curs.execute("SELECT %s::TEXT AS foo", (u'àèìòù',))
x = curs.fetchone()[0]
print " ->", unicode(x, 'latin-1').encode('utf-8'), type(x)
print "Setting backend encoding to UTF8 and executing queries:"
conn.set_client_encoding('UNICODE')
curs = conn.cursor()
curs.execute("SELECT %s::TEXT AS foo", (u'àèìòù'.encode('utf-8'),))
x = curs.fetchone()[0]
print " ->", x, type(x)
curs.execute("SELECT %s::TEXT AS foo", (u'àèìòù',))
x = curs.fetchone()[0]
print " ->", x, type(x)
print "Using UNICODE typecaster"
psycopg.extensions.register_type(psycopg.extensions.UNICODE)
print "Setting backend encoding to LATIN1 and executing queries:"
conn.set_client_encoding('LATIN1')
curs = conn.cursor()
curs.execute("SELECT %s::TEXT AS foo", ('àèìòù',))
x = curs.fetchone()[0]
print " ->", x.encode('utf-8'), ":", type(x)
curs.execute("SELECT %s::TEXT AS foo", (u'àèìòù',))
x = curs.fetchone()[0]
print " ->", x.encode('utf-8'), ":", type(x)
print "Setting backend encoding to UTF8 and executing queries:"
conn.set_client_encoding('UNICODE')
curs = conn.cursor()
curs.execute("SELECT %s::TEXT AS foo", (u'àèìòù'.encode('utf-8'),))
x = curs.fetchone()[0]
print " ->", x.encode('utf-8'), ":", type(x)
curs.execute("SELECT %s::TEXT AS foo", (u'àèìòù',))
x = curs.fetchone()[0]
print " ->", x.encode('utf-8'), ":", type(x)
print "Executing full UNICODE queries"
print "Setting backend encoding to LATIN1 and executing queries:"
conn.set_client_encoding('LATIN1')
curs = conn.cursor()
curs.execute(u"SELECT %s::TEXT AS foo", ('àèìòù',))
x = curs.fetchone()[0]
print " ->", x.encode('utf-8'), ":", type(x)
curs.execute(u"SELECT %s::TEXT AS foo", (u'àèìòù',))
x = curs.fetchone()[0]
print " ->", x.encode('utf-8'), ":", type(x)
print "Setting backend encoding to UTF8 and executing queries:"
conn.set_client_encoding('UNICODE')
curs = conn.cursor()
curs.execute(u"SELECT %s::TEXT AS foo", (u'àèìòù'.encode('utf-8'),))
x = curs.fetchone()[0]
print " ->", x.encode('utf-8'), ":", type(x)
curs.execute(u"SELECT %s::TEXT AS foo", (u'àèìòù',))
x = curs.fetchone()[0]
print " ->", x.encode('utf-8'), ":", type(x)

59
examples/lastrowid.py Normal file
View File

@ -0,0 +1,59 @@
# lastrowid.py - example of using .lastrowid attribute
#
# Copyright (C) 2001-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## don't modify anything below tis line (except for experimenting)
import sys, psycopg
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
curs = conn.cursor()
try:
curs.execute("CREATE TABLE test_oid (name text, surname text)")
except:
conn.rollback()
curs.execute("DROP TABLE test_oid")
curs.execute("CREATE TABLE test_oid (name text, surname text)")
conn.commit()
data = ({'name':'Federico', 'surname':'Di Gregorio'},
{'name':'Pierluigi', 'surname':'Di Nunzio'})
curs.execute("""INSERT INTO test_oid
VALUES (%(name)s, %(surname)s)""", data[0])
foid = curs.lastrowid
print "Oid for %(name)s %(surname)s" % data[0], "is", foid
curs.execute("""INSERT INTO test_oid
VALUES (%(name)s, %(surname)s)""", data[1])
moid = curs.lastrowid
print "Oid for %(name)s %(surname)s" % data[1], "is", moid
curs.execute("SELECT * FROM test_oid WHERE oid = %d", (foid,))
print "Oid", foid, "selected %s %s" % curs.fetchone()
curs.execute("SELECT * FROM test_oid WHERE oid = %d", (moid,))
print "Oid", moid, "selected %s %s" % curs.fetchone()
curs.execute("DROP TABLE test_oid")
conn.commit()

47
examples/mogrify.py Normal file
View File

@ -0,0 +1,47 @@
# mogrify.py - test all possible type mogrifications
# -*- encoding: latin1 -*-
#
# Copyright (C) 2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## don't modify anything below this line (except for experimenting)
import sys, psycopg
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
print "Encoding for this connection is", conn.encoding
curs = conn.cursor()
curs.execute("SELECT %(foo)s AS foo", {'foo':'bar'})
curs.execute("SELECT %(foo)s AS foo", {'foo':None})
curs.execute("SELECT %(foo)s AS foo", {'foo':True})
curs.execute("SELECT %(foo)f AS foo", {'foo':42})
curs.execute("SELECT %(foo)s AS foo", {'foo':u'yattà!'})
curs.execute("SELECT %(foo)s AS foo", {'foo':u'bar'})
print curs.mogrify("SELECT %(foo)s AS foo", {'foo':'bar'})
print curs.mogrify("SELECT %(foo)s AS foo", {'foo':None})
print curs.mogrify("SELECT %(foo)s AS foo", {'foo':True})
print curs.mogrify("SELECT %(foo)f AS foo", {'foo':42})
print curs.mogrify("SELECT %(foo)s AS foo", {'foo':u'yattà!'})
print curs.mogrify("SELECT %(foo)s AS foo", {'foo':u'bar'})
conn.rollback()

117
examples/myfirstrecipe.py Normal file
View File

@ -0,0 +1,117 @@
"""
Using a tuple as a bound variable in "SELECT ... IN (...)" clauses
in PostgreSQL using psycopg 2
Some time ago someone asked on the psycopg mailing list how to have a
bound variable expand to the right SQL for an SELECT IN clause:
SELECT * FROM atable WHERE afield IN (value1, value2, value3)
with the values to be used in the IN clause to be passed to the cursor
.execute() method in a tuple as a bound variable, i.e.:
in_values = ("value1", "value2", "value3")
curs.execute("SELECT ... IN %s", (in_values,))
psycopg 1 does support typecasting from Python to PostgreSQL (and back)
only for simple types and this problem has no elegant solution (short or
writing a wrapper class returning the pre-quoted text in an __str__
method.
But psycopg 2 offers a simple and elegant solution by partially
implementing the Object Adaptation from PEP 246. psycopg 2 (still in
beta and currently labeled as 1.99.9) moves the type-casting logic into
external adapters and a somehow broken adapt() function.
While the original adapt() takes 3 arguments, psycopg's one only takes
1: the bound variable to be adapted. The result is an object supporting
a not-yet well defined protocol that we can call IPsycopgSQLQuote:
class IPsycopgSQLQuote:
def getquoted(self):
"Returns a quoted string representing the bound variable."
def getbinary(self):
"Returns a binary quoted string representing the bound variable."
def getbuffer(self):
"Returns the wrapped object itself."
__str__ = getquoted
Then one of the functions (usually .getquoted()) is called by psycopg at
the right time to obtain the right, sql-quoted representation for the
corresponding bound variable.
The nice part is that the default, built-in adapters, derived from
psycopg 1 tyecasting code can be overridden by the programmer, simply
replacing them in the psycopg.extensions.adapters dictionary.
Then the solution to the original problem is now obvious: write an
adapter that adapts tuple objects into the right SQL string, by calling
recursively adapt() on each element.
Note: psycopg 2 adapter code is still very young and will probably move
to a more 'standard' (3 arguments) implementation for the adapt()
function; as long as that does not slow down too much query execution.
Psycopg 2 development can be tracked on the psycopg mailing list:
http://lists.initd.org/mailman/listinfo/psycopg
and on the psycopg 2 wiki:
http://wiki.initd.org/Projects/Psycopg2
"""
import psycopg
import psycopg.extensions
from psycopg.extensions import adapt as psycoadapt
class AsIs(object):
"""An adapter that just return the object 'as is'.
psycopg 1.99.9 has some optimizations that make impossible to call
adapt() without adding some basic adapters externally. This limitation
will be lifted in a future release.
"""
def __init__(self, obj):
self.__obj = obj
def getquoted(self):
return self.__obj
class SQL_IN(object):
"""Adapt a tuple to an SQL quotable object."""
def __init__(self, seq):
self._seq = seq
def getquoted(self):
# this is the important line: note how every object in the
# list is adapted and then how getquoted() is called on it
qobjs = [str(psycoadapt(o).getquoted()) for o in self._seq]
return '(' + ', '.join(qobjs) + ')'
__str__ = getquoted
# add our new adapter class to psycopg list of adapters
psycopg.extensions.adapters[tuple] = SQL_IN
psycopg.extensions.adapters[float] = AsIs
psycopg.extensions.adapters[int] = AsIs
# usually we would call:
#
# conn = psycopg.connect("...")
# curs = conn.cursor()
# curs.execute("SELECT ...", (("this", "is", "the", "tuple"),))
#
# but we have no connection to a database right now, so we just check
# the SQL_IN class by calling psycopg's adapt() directly:
if __name__ == '__main__':
print "Note how the string will be SQL-quoted, but the number will not:"
print psycoadapt(("this is an 'sql quoted' str\\ing", 1, 2.0))

52
examples/simple.py Normal file
View File

@ -0,0 +1,52 @@
# simple.py - very simple example of plain DBAPI-2.0 usage
# currently used as test-me-stress-me script for psycopg 2.0
#
# Copyright (C) 2001-2003 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## don't modify anything below this line (except for experimenting)
class SimpleQuoter(object):
def sqlquote(x=None):
return "'bar'"
import sys, psycopg
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
print "Encoding for this connection is", conn.encoding
curs = conn.cursor()
curs.execute("SELECT 1 AS foo")
print curs.fetchone()
curs.execute("SELECT 1 AS foo")
print curs.fetchmany()
curs.execute("SELECT 1 AS foo")
print curs.fetchall()
conn.rollback()
sys.exit(0)
curs.execute("SELECT 1 AS foo", async=1)
curs.execute("SELECT %(foo)s AS foo", {'foo':'bar'})
curs.execute("SELECT %(foo)s AS foo", {'foo':None})
curs.execute("SELECT %(foo)f AS foo", {'foo':42})
curs.execute("SELECT %(foo)s AS foo", {'foo':SimpleQuoter()})

BIN
examples/somehackers.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

160
examples/threads.py Normal file
View File

@ -0,0 +1,160 @@
# threads.py -- example of multiple threads using psycopg
# -*- encoding: latin1 -*-
#
# Copyright (C) 2001-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## some others parameters
INSERT_THREADS = ('A', 'B', 'C')
SELECT_THREADS = ('1', '2')
ROWS = 1000
COMMIT_STEP = 20
SELECT_SIZE = 10000
SELECT_STEP = 500
SELECT_DIV = 250
# the available modes are:
# 0 - one connection for all insert and one for all select threads
# 1 - connections generated using the connection pool
MODE = 1
## don't modify anything below tis line (except for experimenting)
import sys, psycopg, threading
from psycopg.pool import ThreadedConnectionPool
if len(sys.argv) > 1:
DSN = sys.argv[1]
if len(sys.argv) > 2:
MODE = int(sys.argv[2])
print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
curs = conn.cursor()
try:
curs.execute("""CREATE TABLE test_threads (
name text, value1 int4, value2 float)""")
except:
conn.rollback()
curs.execute("DROP TABLE test_threads")
curs.execute("""CREATE TABLE test_threads (
name text, value1 int4, value2 float)""")
conn.commit()
## this function inserts a big number of rows and creates and destroys
## a large number of cursors
def insert_func(conn_or_pool, rows):
name = threading.currentThread().getName()
if MODE == 0:
conn = conn_or_pool
else:
conn = conn_or_pool.getconn()
for i in range(rows):
if divmod(i, COMMIT_STEP)[1] == 0:
conn.commit()
if MODE == 1:
conn_or_pool.putconn(conn)
s = name + ": COMMIT STEP " + str(i)
print s
if MODE == 1:
conn = conn_or_pool.getconn()
c = conn.cursor()
try:
c.execute("INSERT INTO test_threads VALUES (%s, %d, %f)",
(str(i), i, float(i)))
except psycopg.ProgrammingError, err:
print name, ": an error occurred; skipping this insert"
print err
conn.commit()
## a nice select function that prints the current number of rows in the
## database (and transefer them, putting some pressure on the network)
def select_func(conn_or_pool, z):
name = threading.currentThread().getName()
if MODE == 0:
conn = conn_or_pool
conn.set_isolation_level(0)
for i in range(SELECT_SIZE):
if divmod(i, SELECT_STEP)[1] == 0:
try:
if MODE == 1:
conn = conn_or_pool.getconn()
conn.set_isolation_level(0)
c = conn.cursor()
c.execute("SELECT * FROM test_threads WHERE value2 < %s",
(int(i/z),))
l = c.fetchall()
if MODE == 1:
conn_or_pool.putconn(conn)
s = name + ": number of rows fetched: " + str(len(l))
print s
except psycopg.ProgrammingError, err:
print name, ": an error occurred; skipping this select"
print err
## create the connection pool or the connections
if MODE == 0:
conn_insert = psycopg.connect(DSN)
conn_select = psycopg.connect(DSN)
else:
m = len(INSERT_THREADS) + len(SELECT_THREADS)
n = m/2
conn_insert = conn_select = ThreadedConnectionPool(n, m, DSN)
## create the threads
threads = []
print "Creating INSERT threads:"
for name in INSERT_THREADS:
t = threading.Thread(None, insert_func, 'Thread-'+name,
(conn_insert, ROWS))
t.setDaemon(0)
threads.append(t)
print "Creating SELECT threads:"
for name in SELECT_THREADS:
t = threading.Thread(None, select_func, 'Thread-'+name,
(conn_select, SELECT_DIV))
t.setDaemon(0)
threads.append(t)
## really start the threads now
for t in threads:
t.start()
# and wait for them to finish
for t in threads:
t.join()
print t.getName(), "exited OK"
conn.commit()
curs.execute("SELECT count(name) FROM test_threads")
print "Inserted", curs.fetchone()[0], "rows."
curs.execute("DROP TABLE test_threads")
conn.commit()

62
examples/tz.py Normal file
View File

@ -0,0 +1,62 @@
# tz.py - example of datetime objects with time zones
# -*- encoding: latin1 -*-
#
# Copyright (C) 2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
## put in DSN your DSN string
DSN = 'dbname=test'
## don't modify anything below this line (except for experimenting)
import sys, psycopg
import datetime
from psycopg.tz import ZERO, LOCAL, FixedOffsetTimezone
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
curs = conn.cursor()
try:
curs.execute("CREATE TABLE test_tz (t timestamp with time zone)")
except:
conn.rollback()
curs.execute("DROP TABLE test_tz")
curs.execute("CREATE TABLE test_tz (t timestamp with time zone)")
conn.commit()
d = datetime.datetime(1971, 10, 19, 22, 30, 0, tzinfo=LOCAL)
curs.execute("INSERT INTO test_tz VALUES (%s)", (d,))
print "Inserted timestamp with timezone:", d
print "Time zone:", d.tzinfo.tzname(d), "offset:", d.tzinfo.utcoffset(d)
tz = FixedOffsetTimezone(-5*60, "EST")
d = datetime.datetime(1971, 10, 19, 22, 30, 0, tzinfo=tz)
curs.execute("INSERT INTO test_tz VALUES (%s)", (d,))
print "Inserted timestamp with timezone:", d
print "Time zone:", d.tzinfo.tzname(d), "offset:", d.tzinfo.utcoffset(d)
curs.tzinfo_factory = FixedOffsetTimezone
curs.execute("SELECT * FROM test_tz")
for d in curs:
u = d[0].utcoffset() or ZERO
print "UTC time: ", d[0] - u
print "Local time:", d[0]
print "Time zone:", d[0].tzinfo.tzname(d[0]), d[0].tzinfo.utcoffset(d[0])
curs.execute("DROP TABLE test_tz")
conn.commit()

BIN
examples/whereareyou.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

28
lib/__init__.py Normal file
View File

@ -0,0 +1,28 @@
# psycopg/__init__.py - initialization of the psycopg module
#
# Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
__all__ = ['extensions', 'extras', 'tz', 'pool']
# import the DBAPI-2.0 stuff into top-level module
from _psycopg import BINARY, NUMBER, STRING, DATETIME, ROWID
from _psycopg import Binary, Date, Time, Timestamp
from _psycopg import DateFromTicks, TimeFromTicks, TimestampFromTicks
from _psycopg import Error, Warning, DataError, DatabaseError, ProgrammingError
from _psycopg import IntegrityError, InterfaceError, InternalError
from _psycopg import NotSupportedError, OperationalError
from _psycopg import connect, apilevel, threadsafety, paramstyle
from _psycopg import __version__

31
lib/extensions.py Normal file
View File

@ -0,0 +1,31 @@
# psycopg/extensions.py - DBAPI-2.0 extensions specific to psycopg
#
# Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
from _psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT
from _psycopg import TIME, DATE, INTERVAL
from _psycopg import Boolean, QuotedString
try:
from _psycopg import DateFromMx, TimeFromMx, TimestampFromMx
from _psycopg import IntervalFromMx
except:
pass
try:
from _psycopg import DateFromPy, TimeFromPy, TimestampFromPy
from _psycopg import IntervalFromPy
except:
pass
from _psycopg import adapt, adapters, encodings, connection, cursor
from _psycopg import string_types, binary_types, new_type, register_type

59
lib/extras.py Normal file
View File

@ -0,0 +1,59 @@
# psycopg/extras.py - miscellaneous extra goodies for psycopg
#
# Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
from psycopg.extensions import cursor as _cursor
class DictCursor(_cursor):
"""A cursor that keeps a list of column name -> index mappings."""
__query_executed = 0
def execute(self, query, vars=None, async=0):
self.tuple_factory = DictRow
self.index = {}
self.__query_executed = 1
return _cursor.execute(self, query, vars, async)
def _build_index(self):
if self.description:
for i in range(len(self.description)):
self.index[self.description[i][0]] = i
def fetchone(self):
if self.__query_executed:
self._build_index()
return _cursor.fetchone(self)
def fetchmany(self, size=None):
if self.__query_executed:
self._build_index()
return _cursor.fetchmany(self, size)
def fetchall(self):
if self.__query_executed:
self._build_index()
return _cursor.fetchall(self)
class DictRow(list):
"""A row object that allow by-colun-name access to data."""
def __init__(self, cursor):
self._cursor = cursor
self[:] = [None] * len(cursor.description)
print cursor, self
def __getitem__(self, x):
if type(x) != int:
x = self._cursor.index[x]
return list.__getitem__(self, x)

184
lib/pool.py Normal file
View File

@ -0,0 +1,184 @@
# psycopg/pool.py - pooling code for psycopg
#
# Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
import psycopg
try:
from zLOG import LOG, DEBUG, INFO
def dbg(*args):
LOG('ZPsycopgDA', DEBUG, "",
' '.join([str(x) for x in args])+'\n')
LOG('ZPsycopgDA', INFO, "Installed", "Logging using Zope's zLOG\n")
except:
import sys
def dbg(*args):
sys.stderr.write(' '.join(args)+'\n')
class PoolError(psycopg.Error):
pass
class AbstractConnectionPool(object):
"""Generic key-based pooling code."""
def __init__(self, minconn, maxconn, *args, **kwargs):
"""Initialize the connection pool.
New 'minconn' connections are created immediately calling 'connfunc'
with given parameters. The connection pool will support a maximum of
about 'maxconn' connections.
"""
self.minconn = minconn
self.maxconn = maxconn
self.closed = False
self._args = args
self._kwargs = kwargs
self._pool = []
self._used = {}
self._keys = 0
for i in range(self.minconn):
self._connect()
def _connect(self, key=None):
"""Create a new connection and assign it to 'key' if not None."""
conn = psycopg.connect(*self._args, **self._kwargs)
if key is not None:
self._used[key] = conn
else:
self._pool.append(conn)
return conn
def _getkey(self):
"""Return a new unique key."""
self._keys += 1
return self._keys
def _findkey(self, conn):
"""Return the key associated with a connection or None."""
for o, k in self._used.items():
if o == conn:
return k
def _getconn(self, key=None):
"""Get a free connection and assign it to 'key' if not None."""
if self.closed: raise PoolError("connection pool is closed")
if key is None: key = self._getkey()
if not self._used.has_key(key):
if not self._pool:
if len(self._used) == self.maxconn:
raise PoolError("connection pool exausted")
return self._connect(key)
else:
self._used[key] = self._pool.pop()
return self._used[key]
def _putconn(self, conn, key=None, close=False):
"""Put away a connection."""
if self.closed: raise PoolError("connection pool is closed")
if key is None: key = self._findkey(conn)
if not key:
raise PoolError("trying to put unkeyed connection")
if len(self._pool) < self.minconn and not close:
self._pool.append(conn)
else:
conn.close()
# here we check for the presence of key because it can happen that a
# thread tries to put back a connection after a call to close
if not self.closed or key in self._used:
del self._used[key]
def _closeall(self):
"""Close all connections.
Note that this can lead to some code fail badly when trying to use
an already closed connection. If you call .closeall() make sure
your code can deal with it.
"""
if self.closed: raise PoolError("connection pool is closed")
for conn in self._pool + list(self._used.values()):
try:
print "Closing connection", conn
conn.close()
except:
pass
self.closed = True
class SimpleConnectionPool(AbstractConnectionPool):
"""A connection pool that can't be shared across different threads."""
getconn = AbstractConnectionPool._getconn
putconn = AbstractConnectionPool._putconn
closeall = AbstractConnectionPool._closeall
class ThreadedConnectionPool(AbstractConnectionPool):
"""A connection pool that works with the threading module.
Note that this connection pool generates by itself the required keys
using the current thread id. This means that untill a thread put away
a connection it will always get the same connection object by successive
.getconn() calls.
"""
def __init__(self, minconn, maxconn, *args, **kwargs):
"""Initialize the threading lock."""
import threading
AbstractConnectionPool.__init__(
self, minconn, maxconn, *args, **kwargs)
self._lock = threading.Lock()
# we we'll need the thread module, to determine thread ids, so we
# import it here and copy it in an instance variable
import thread
self.__thread = thread
def getconn(self):
"""Generate thread id and return a connection."""
key = self.__thread.get_ident()
self._lock.acquire()
try:
return self._getconn(key)
finally:
self._lock.release()
def putconn(self, conn=None, close=False):
"""Put away an unused connection."""
key = self.__thread.get_ident()
self._lock.acquire()
try:
if not conn: conn = self._used[key]
self._putconn(conn, key, close)
finally:
self._lock.release()
def closeall(self):
"""Close all connections (even the one currently in use."""
self._lock.acquire()
try:
self._closeall()
finally:
self._lock.release()

94
lib/tz.py Normal file
View File

@ -0,0 +1,94 @@
# psycopg/tz.py - tzinfo implementation
#
# Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
import datetime
import time
ZERO = datetime.timedelta(0)
class FixedOffsetTimezone(datetime.tzinfo):
"""Fixed offset in minutes east from UTC.
This is exactly the implementation found in Python 2.3.x documentation,
with a small change to the __init__ method to allow for pickling and a
default name in the form 'sHH:MM' ('s' is the sign.)
"""
_name = None
_offset = ZERO
def __init__(self, offset=None, name=None):
if offset is not None:
self._offset = datetime.timedelta(minutes = offset)
if name is not None:
self._name = name
def utcoffset(self, dt):
return self._offset
def tzname(self, dt):
if self._name is not None:
return self._name
else:
seconds = self._offset.seconds + self._offset.days * 86400
hours, seconds = divmod(seconds, 3600)
minutes = seconds/60
if minutes:
return "%+03d:%d" % (hours, minutes)
else:
return "%+03d" % hours
def dst(self, dt):
return ZERO
STDOFFSET = datetime.timedelta(seconds = -time.timezone)
if time.daylight:
DSTOFFSET = datetime.timedelta(seconds = -time.altzone)
else:
DSTOFFSET = STDOFFSET
DSTDIFF = DSTOFFSET - STDOFFSET
class LocalTimezone(datetime.tzinfo):
"""Platform idea of local timezone.
This is the exact implementation from the Pyhton 2.3 documentation.
"""
def utcoffset(self, dt):
if self._isdst(dt):
return DSTOFFSET
else:
return STDOFFSET
def dst(self, dt):
if self._isdst(dt):
return DSTDIFF
else:
return ZERO
def tzname(self, dt):
return time.tzname[self._isdst(dt)]
def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
dt.weekday(), 0, -1)
stamp = time.mktime(tt)
tt = time.localtime(stamp)
return tt.tm_isdst > 0
LOCAL = LocalTimezone()
# TODO: pre-generate some interesting time zones?

336
psycopg/adapter_binary.c Normal file
View File

@ -0,0 +1,336 @@
/* adapter_binary.c - Binary objects
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <libpq-fe.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_binary.h"
/** the quoting code */
#ifndef PSYCOPG_OWN_QUOTING
#define binary_escape PQescapeBytea
#else
static unsigned char *
binary_escape(char *from, size_t from_length, size_t *to_length)
{
unsigneed char *quoted, *chptr, *newptr;
int i, space, new_space;
space = from_length + 2;
Py_BEGIN_ALLOW_THREADS;
quoted = (unsigned char*)calloc(space, sizeof(char));
if (quoted == NULL) return NULL;
chptr = quoted;
for (i=0; i < len; i++) {
if (chptr - quoted > space - 6) {
new_space = space * ((space) / (i + 1)) + 2 + 6;
if (new_space - space < 1024) space += 1024;
else space = new_space;
newptr = (unsigned char *)realloc(quoted, space);
if (newptr == NULL) {
free(quoted);
return NULL;
}
/* chptr has to be moved to the new location*/
chptr = newptr + (chptr - quoted);
quoted = newptr;
Dprintf("binary_escape: reallocated %i bytes at %p", space,quoted);
}
if (from[i]) {
if (from[i] >= ' ' && from[i] <= '~') {
if (from[i] == '\'') {
*chptr = '\\';
chptr++;
*chptr = '\'';
chptr++;
}
else if (from[i] == '\\') {
memcpy(chptr, "\\\\\\\\", 4);
chptr += 4;
}
else {
/* leave it as it is if ascii printable */
*chptr = from[i];
chptr++;
}
}
else {
unsigned char c;
/* escape to octal notation \nnn */
*chptr++ = '\\';
*chptr++ = '\\';
c = from[i];
*chptr = ((c >> 6) & 0x07) + 0x30; chptr++;
*chptr = ((c >> 3) & 0x07) + 0x30; chptr++;
*chptr = ( c & 0x07) + 0x30; chptr++;
}
}
else {
/* escape null as \\000 */
memcpy(chptr, "\\\\000", 5);
chptr += 5;
}
}
*chptr = '\0';
Py_END_ALLOW_THREADS;
*to_size = chptr - quoted + 1;
return quoted;
}
#endif
/* binary_quote - do the quote process on plain and unicode strings */
static PyObject *
binary_quote(binaryObject *self)
{
char *to;
const char *buffer;
int buffer_len;
size_t len = 0;
/* if we got a plain string or a buffer we escape it and save the buffer */
if (PyString_Check(self->wrapped) || PyBuffer_Check(self->wrapped)) {
/* escape and build quoted buffer */
PyObject_AsCharBuffer(self->wrapped, &buffer, &buffer_len);
to = (char *)binary_escape(buffer, buffer_len, &len);
if (to == NULL) {
PyErr_NoMemory();
return NULL;
}
self->buffer = PyString_FromFormat("'%s'", to);
PQfreemem(to);
}
/* if the wrapped object is not a string or a buffer, this is an error */
else {
PyErr_SetString(PyExc_TypeError, "can't escape non-string object");
return NULL;
}
return self->buffer;
}
/* binary_str, binary_getquoted - return result of quoting */
static PyObject *
binary_str(binaryObject *self)
{
if (self->buffer == NULL) {
binary_quote(self);
}
Py_INCREF(self->buffer);
return self->buffer;
}
PyObject *
binary_getquoted(binaryObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
return binary_str(self);
}
PyObject *
binary_prepare(binaryObject *self, PyObject *args)
{
PyObject *fake;
if (!PyArg_ParseTuple(args, "O", &fake)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/** the Binary object **/
/* object member list */
static struct PyMemberDef binaryObject_members[] = {
{"adapted", T_OBJECT, offsetof(binaryObject, wrapped), RO},
{"buffer", T_OBJECT, offsetof(binaryObject, buffer), RO},
{NULL}
};
/* object method table */
static PyMethodDef binaryObject_methods[] = {
{"getquoted", (PyCFunction)binary_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL-quoted binary string"},
{"prepare", (PyCFunction)binary_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},
{NULL} /* Sentinel */
};
/* initialization and finalization methods */
static int
binary_setup(binaryObject *self, PyObject *str)
{
Dprintf("binary_setup: init binary object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
self->buffer = NULL;
self->wrapped = str;
Py_INCREF(self->wrapped);
Dprintf("binary_setup: good binary object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
return 0;
}
static void
binary_dealloc(PyObject* obj)
{
binaryObject *self = (binaryObject *)obj;
Py_XDECREF(self->wrapped);
Py_XDECREF(self->buffer);
Dprintf("binary_dealloc: deleted binary object at %p, refcnt = %d",
obj, obj->ob_refcnt);
obj->ob_type->tp_free(obj);
}
static int
binary_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *str;
if (!PyArg_ParseTuple(args, "O", &str))
return -1;
return binary_setup((binaryObject *)obj, str);
}
static PyObject *
binary_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
binary_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject *
binary_repr(binaryObject *self)
{
return PyString_FromFormat("<psycopg.Binary object at %p>", self);
}
/* object type */
#define binaryType_doc \
"psycopg.Binary(buffer) -> new binary object"
PyTypeObject binaryType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg.Binary",
sizeof(binaryObject),
0,
binary_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)binary_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)binary_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
binaryType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
binaryObject_methods, /*tp_methods*/
binaryObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
binary_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
binary_new, /*tp_new*/
(freefunc)binary_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};
/** module-level functions **/
PyObject *
psyco_Binary(PyObject *module, PyObject *args)
{
PyObject *str;
if (!PyArg_ParseTuple(args, "O", &str))
return NULL;
return PyObject_CallFunction((PyObject *)&binaryType, "O", str);
}

52
psycopg/adapter_binary.h Normal file
View File

@ -0,0 +1,52 @@
/* adapter_binary.h - definition for the Binary type
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_BINARY_H
#define PSYCOPG_BINARY_H 1
#include <Python.h>
#include <libpq-fe.h>
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject binaryType;
typedef struct {
PyObject HEAD;
PyObject *wrapped;
PyObject *buffer;
char *encoding;
} binaryObject;
/* functions exported to psycopgmodule.c */
extern PyObject *psyco_Binary(PyObject *module, PyObject *args);
#define psyco_Binary_doc \
"psycopg.Binary(buffer) -> new binary object"
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_BINARY_H) */

439
psycopg/adapter_datetime.c Normal file
View File

@ -0,0 +1,439 @@
/* adapter_datetime.c - python date/time objects
*
* Copyright (C) 2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <datetime.h>
#include <time.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_datetime.h"
/* the pointer to the datetime module API is initialized by the module init
code, we just need to grab it */
extern PyObject* pyDateTimeModuleP;
extern PyObject *pyDateTypeP;
extern PyObject *pyTimeTypeP;
extern PyObject *pyDateTimeTypeP;
extern PyObject *pyDeltaTypeP;
/* datetime_str, datetime_getquoted - return result of quoting */
static PyObject *
pydatetime_str(pydatetimeObject *self)
{
if (self->type <= PSYCO_DATETIME_TIMESTAMP) {
PyObject *res = NULL;
PyObject *iso = PyObject_CallMethod(self->wrapped, "isoformat", NULL);
if (iso) {
res = PyString_FromFormat("'%s'", PyString_AsString(iso));
Py_DECREF(iso);
}
return res;
}
else {
PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped;
char buffer[8];
int i, j, x;
int a = obj->microseconds;
for (i=1000000, j=0; i > 0 ; i /= 10) {
x = a/i;
a -= x*i;
buffer[j++] = '0'+x;
}
buffer[j] = '\0';
return PyString_FromFormat("'%d days %d.%s seconds'",
obj->days, obj->seconds, buffer);
}
}
PyObject *
pydatetime_getquoted(pydatetimeObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
return pydatetime_str(self);
}
PyObject *
pydatetime_prepare(pydatetimeObject *self, PyObject *args)
{
PyObject *fake;
if (!PyArg_ParseTuple(args, "O", &fake)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/** the DateTime wrapper object **/
/* object member list */
static struct PyMemberDef pydatetimeObject_members[] = {
{"adapted", T_OBJECT, offsetof(pydatetimeObject, wrapped), RO},
{"type", T_INT, offsetof(pydatetimeObject, type), RO},
{NULL}
};
/* object method table */
static PyMethodDef pydatetimeObject_methods[] = {
{"getquoted", (PyCFunction)pydatetime_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL date/time"},
{"prepare", (PyCFunction)pydatetime_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},
{NULL} /* Sentinel */
};
/* initialization and finalization methods */
static int
pydatetime_setup(pydatetimeObject *self, PyObject *obj, int type)
{
Dprintf("pydatetime_setup: init datetime object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
self->type = type;
self->wrapped = obj;
Py_INCREF(self->wrapped);
Dprintf("pydatetime_setup: good pydatetime object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
return 0;
}
static void
pydatetime_dealloc(PyObject* obj)
{
pydatetimeObject *self = (pydatetimeObject *)obj;
Py_XDECREF(self->wrapped);
Dprintf("mpydatetime_dealloc: deleted pydatetime object at %p, "
"refcnt = %d", obj, obj->ob_refcnt);
obj->ob_type->tp_free(obj);
}
static int
pydatetime_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *dt;
int type = -1; /* raise an error if type was not passed! */
if (!PyArg_ParseTuple(args, "O|i", &dt, &type))
return -1;
return pydatetime_setup((pydatetimeObject *)obj, dt, type);
}
static PyObject *
pydatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
pydatetime_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject *
pydatetime_repr(pydatetimeObject *self)
{
return PyString_FromFormat("<psycopg.datetime object at %p>", self);
}
/* object type */
#define pydatetimeType_doc \
"psycopg.Pydatetime(datetime, type) -> new datetime wrapper object"
PyTypeObject pydatetimeType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg.datetime",
sizeof(pydatetimeObject),
0,
pydatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)pydatetime_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)pydatetime_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
pydatetimeType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pydatetimeObject_methods, /*tp_methods*/
pydatetimeObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
pydatetime_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
pydatetime_new, /*tp_new*/
(freefunc)pydatetime_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};
/** module-level functions **/
#ifdef PSYCOPG_DEFAULT_PYDATETIME
PyObject *
psyco_Date(PyObject *self, PyObject *args)
{
PyObject *res = NULL;
int year, month, day;
PyObject* obj = NULL;
if (!PyArg_ParseTuple(args, "iii", &year, &month, &day))
return NULL;
obj = PyObject_CallFunction(pyDateTypeP, "iii", year, month, day);
if (obj) {
res = PyObject_CallFunction((PyObject *)&pydatetimeType,
"Oi", obj, PSYCO_DATETIME_DATE);
Py_DECREF(obj);
}
return res;
}
PyObject *
psyco_Time(PyObject *self, PyObject *args)
{
PyObject *res = NULL;
int hours, minutes=0;
double micro, seconds=0.0;
PyObject* obj = NULL;
if (!PyArg_ParseTuple(args, "iid", &hours, &minutes, &seconds))
return NULL;
micro = (seconds - floor(seconds)) * 1000000.0;
obj = PyObject_CallFunction(pyTimeTypeP, "iiii",
hours, minutes, (int)round(seconds), (int)round(micro));
if (obj) {
res = PyObject_CallFunction((PyObject *)&pydatetimeType,
"Oi", obj, PSYCO_DATETIME_TIME);
Py_DECREF(obj);
}
return res;
}
PyObject *
psyco_Timestamp(PyObject *self, PyObject *args)
{
PyObject *res = NULL;
int year, month, day;
int hour=0, minute=0; /* default to midnight */
double micro, second=0.0;
PyObject* obj = NULL;
if (!PyArg_ParseTuple(args, "lii|iid", &year, &month, &day,
&hour, &minute, &second))
return NULL;
micro = (second - floor(second)) * 1000000.0;
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiii",
year, month, day, hour, minute, (int)round(second), (int)round(micro));
if (obj) {
res = PyObject_CallFunction((PyObject *)&pydatetimeType,
"Oi", obj, PSYCO_DATETIME_TIMESTAMP);
Py_DECREF(obj);
}
return res;
}
PyObject *
psyco_DateFromTicks(PyObject *self, PyObject *args)
{
PyObject *res = NULL;
struct tm tm;
time_t t;
double ticks;
if (!PyArg_ParseTuple(args, "d", &ticks))
return NULL;
t = (time_t)round(ticks);
if (gmtime_r(&t, &tm)) {
args = Py_BuildValue("iii", tm.tm_year, tm.tm_mon, tm.tm_mday);
if (args) {
res = psyco_Date(self, args);
Py_DECREF(args);
}
}
return res;
}
PyObject *
psyco_TimeFromTicks(PyObject *self, PyObject *args)
{
PyObject *res = NULL;
struct tm tm;
time_t t;
double ticks;
if (!PyArg_ParseTuple(args,"d", &ticks))
return NULL;
t = (time_t)round(ticks);
if (gmtime_r(&t, &tm)) {
args = Py_BuildValue("iid", tm.tm_hour, tm.tm_min, (double)tm.tm_sec);
if (args) {
res = psyco_Time(self, args);
Py_DECREF(args);
}
}
return res;
}
PyObject *
psyco_TimestampFromTicks(PyObject *self, PyObject *args)
{
PyObject *res = NULL;
struct tm tm;
time_t t;
double ticks;
if (!PyArg_ParseTuple(args,"d", &ticks))
return NULL;
t = (time_t)round(ticks);
if (gmtime_r(&t, &tm)) {
args = Py_BuildValue("iiiiid",
tm.tm_year, tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, (double)tm.tm_sec);
if (args) {
res = psyco_Timestamp(self, args);
Py_DECREF(args);
}
}
return res;
}
#endif
PyObject *
psyco_DateFromPy(PyObject *self, PyObject *args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O!", pyDateTypeP, &obj))
return NULL;
return PyObject_CallFunction((PyObject *)&pydatetimeType, "Oi", obj,
PSYCO_DATETIME_DATE);
}
PyObject *
psyco_TimeFromPy(PyObject *self, PyObject *args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O!", pyTimeTypeP, &obj))
return NULL;
return PyObject_CallFunction((PyObject *)&pydatetimeType, "Oi", obj,
PSYCO_DATETIME_TIME);
}
PyObject *
psyco_TimestampFromPy(PyObject *self, PyObject *args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O!", pyDateTimeTypeP, &obj))
return NULL;
return PyObject_CallFunction((PyObject *)&pydatetimeType, "Oi", obj,
PSYCO_DATETIME_TIMESTAMP);
}
PyObject *
psyco_IntervalFromPy(PyObject *self, PyObject *args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O!", pyDeltaTypeP, &obj))
return NULL;
return PyObject_CallFunction((PyObject *)&pydatetimeType, "Oi", obj,
PSYCO_DATETIME_INTERVAL);
}

View File

@ -0,0 +1,95 @@
/* adapter_datetime.h - definition for the python date/time types
*
* Copyright (C) 2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_DATETIME_H
#define PSYCOPG_DATETIME_H 1
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject pydatetimeType;
typedef struct {
PyObject HEAD;
PyObject *wrapped;
int type;
#define PSYCO_DATETIME_TIME 0
#define PSYCO_DATETIME_DATE 1
#define PSYCO_DATETIME_TIMESTAMP 2
#define PSYCO_DATETIME_INTERVAL 3
} pydatetimeObject;
/* functions exported to psycopgmodule.c */
#ifdef PSYCOPG_DEFAULT_PYDATETIME
extern PyObject *psyco_Date(PyObject *module, PyObject *args);
#define psyco_Date_doc \
"psycopg.Date(year, month, day) -> new date"
extern PyObject *psyco_Time(PyObject *module, PyObject *args);
#define psyco_Time_doc \
"psycopg.Time(hour, minutes, seconds) -> new time"
extern PyObject *psyco_Timestamp(PyObject *module, PyObject *args);
#define psyco_Timestamp_doc \
"psycopg.Time(year, month, day, hour, minutes, seconds) -> new timestamp"
extern PyObject *psyco_DateFromTicks(PyObject *module, PyObject *args);
#define psyco_DateFromTicks_doc \
"psycopg.DateFromTicks(ticks) -> new date"
extern PyObject *psyco_TimeFromTicks(PyObject *module, PyObject *args);
#define psyco_TimeFromTicks_doc \
"psycopg.TimeFromTicks(ticks) -> new date"
extern PyObject *psyco_TimestampFromTicks(PyObject *module, PyObject *args);
#define psyco_TimestampFromTicks_doc \
"psycopg.TimestampFromTicks(ticks) -> new date"
#endif /* PSYCOPG_DEFAULT_PYDATETIME */
extern PyObject *psyco_DateFromPy(PyObject *module, PyObject *args);
#define psyco_DateFromPy_doc \
"psycopg.DateFromPy(datetime.date) -> new wrapper"
extern PyObject *psyco_TimeFromPy(PyObject *module, PyObject *args);
#define psyco_TimeFromPy_doc \
"psycopg.TimeFromPy(datetime.time) -> new wrapper"
extern PyObject *psyco_TimestampFromPy(PyObject *module, PyObject *args);
#define psyco_TimestampFromPy_doc \
"psycopg.TimestampFromPy(datetime.datetime) -> new wrapper"
extern PyObject *psyco_IntervalFromPy(PyObject *module, PyObject *args);
#define psyco_IntervalFromPy_doc \
"psycopg.IntervalFromPy(datetime.timedelta) -> new wrapper"
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_DATETIME_H) */

View File

@ -0,0 +1,392 @@
/* adapter_mxdatetime.c - mx date/time objects
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <mxDateTime.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_mxdatetime.h"
/* the pointer to the mxDateTime API is initialized by the module init code,
we just need to grab it */
extern mxDateTimeModule_APIObject *mxDateTimeP;
/* mxdatetime_str, mxdatetime_getquoted - return result of quoting */
static char *mxdatetimeObject_str_conv[] = {
PSYCO_MXDATETIME_TIME_CONV,
PSYCO_MXDATETIME_DATE_CONV,
PSYCO_MXDATETIME_TIMESTAMP_CONV
};
static PyObject *
mxdatetime_str(mxdatetimeObject *self)
{
return PyObject_CallMethod(self->wrapped, "strftime", "s",
mxdatetimeObject_str_conv[self->type]);
}
PyObject *
mxdatetime_getquoted(mxdatetimeObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
return mxdatetime_str(self);
}
PyObject *
mxdatetime_prepare(mxdatetimeObject *self, PyObject *args)
{
PyObject *fake;
if (!PyArg_ParseTuple(args, "O", &fake)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/** the MxDateTime object **/
/* object member list */
static struct PyMemberDef mxdatetimeObject_members[] = {
{"adapted", T_OBJECT, offsetof(mxdatetimeObject, wrapped), RO},
{"type", T_INT, offsetof(mxdatetimeObject, type), RO},
{NULL}
};
/* object method table */
static PyMethodDef mxdatetimeObject_methods[] = {
{"getquoted", (PyCFunction)mxdatetime_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL date/time"},
{"prepare", (PyCFunction)mxdatetime_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},
{NULL} /* Sentinel */
};
/* initialization and finalization methods */
static int
mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
{
Dprintf("mxdatetime_setup: init mxdatetime object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
self->type = type;
self->wrapped = obj;
Py_INCREF(self->wrapped);
Dprintf("mxdatetime_setup: good mxdatetime object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
return 0;
}
static void
mxdatetime_dealloc(PyObject* obj)
{
mxdatetimeObject *self = (mxdatetimeObject *)obj;
Py_XDECREF(self->wrapped);
Dprintf("mxdatetime_dealloc: deleted mxdatetime object at %p, refcnt = %d",
obj, obj->ob_refcnt);
obj->ob_type->tp_free(obj);
}
static int
mxdatetime_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *mx;
int type = -1; /* raise an error if type was not passed! */
if (!PyArg_ParseTuple(args, "O|i", &mx, &type))
return -1;
return mxdatetime_setup((mxdatetimeObject *)obj, mx, type);
}
static PyObject *
mxdatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
mxdatetime_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject *
mxdatetime_repr(mxdatetimeObject *self)
{
return PyString_FromFormat("<psycopg.MxDateTime object at %p>", self);
}
/* object type */
#define mxdatetimeType_doc \
"psycopg.MxDateTime(mx, type) -> new mx.DateTime wrapper object"
PyTypeObject mxdatetimeType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg.MxDateTime",
sizeof(mxdatetimeObject),
0,
mxdatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)mxdatetime_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)mxdatetime_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
mxdatetimeType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
mxdatetimeObject_methods, /*tp_methods*/
mxdatetimeObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
mxdatetime_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
mxdatetime_new, /*tp_new*/
(freefunc)mxdatetime_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};
/** module-level functions **/
#ifdef PSYCOPG_DEFAULT_MXDATETIME
PyObject *
psyco_Date(PyObject *self, PyObject *args)
{
PyObject *res, *mx;
int year, month, day;
if (!PyArg_ParseTuple(args, "iii", &year, &month, &day))
return NULL;
mx = mxDateTimeP->DateTime_FromDateAndTime(year, month, day, 0, 0, 0.0);
if (mx == NULL) return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_DATE);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_Time(PyObject *self, PyObject *args)
{
PyObject *res, *mx;
int hours, minutes=0;
double seconds=0.0;
if (!PyArg_ParseTuple(args, "iid", &hours, &minutes, &seconds))
return NULL;
mx = mxDateTimeP->DateTimeDelta_FromTime(hours, minutes, seconds);
if (mx == NULL) return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIME);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_Timestamp(PyObject *self, PyObject *args)
{
PyObject *res, *mx;
int year, month, day;
int hour=0, minute=0; /* default to midnight */
double second=0.0;
if (!PyArg_ParseTuple(args, "lii|iid", &year, &month, &day,
&hour, &minute, &second))
return NULL;
mx = mxDateTimeP->DateTime_FromDateAndTime(year, month, day,
hour, minute, second);
if (mx == NULL) return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIMESTAMP);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_DateFromTicks(PyObject *self, PyObject *args)
{
PyObject *res, *mx;
double ticks;
if (!PyArg_ParseTuple(args,"d", &ticks))
return NULL;
if (!(mx = mxDateTimeP->DateTime_FromTicks(ticks)))
return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_DATE);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_TimeFromTicks(PyObject *self, PyObject *args)
{
PyObject *res, *mx, *dt;
double ticks;
if (!PyArg_ParseTuple(args,"d", &ticks))
return NULL;
if (!(dt = mxDateTimeP->DateTime_FromTicks(ticks)))
return NULL;
if (!(mx = mxDateTimeP->DateTimeDelta_FromDaysAndSeconds(
0, ((mxDateTimeObject*)dt)->abstime)))
{
Py_DECREF(dt);
return NULL;
}
Py_DECREF(dt);
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIME);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_TimestampFromTicks(PyObject *self, PyObject *args)
{
PyObject *mx, *res;
double ticks;
if (!PyArg_ParseTuple(args, "d", &ticks))
return NULL;
if (!(mx = mxDateTimeP->DateTime_FromTicks(ticks)))
return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIMESTAMP);
Py_DECREF(mx);
return res;
}
#endif
PyObject *
psyco_DateFromMx(PyObject *self, PyObject *args)
{
PyObject *mx;
if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx))
return NULL;
return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_DATE);
}
PyObject *
psyco_TimeFromMx(PyObject *self, PyObject *args)
{
PyObject *mx;
if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTimeDelta_Type, &mx))
return NULL;
return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIME);
}
PyObject *
psyco_TimestampFromMx(PyObject *self, PyObject *args)
{
PyObject *mx;
if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx))
return NULL;
return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIMESTAMP);
}
PyObject *
psyco_IntervalFromMx(PyObject *self, PyObject *args)
{
PyObject *mx;
if (!PyArg_ParseTuple(args, "O!", mxDateTimeP->DateTime_Type, &mx))
return NULL;
return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_INTERVAL);
}

View File

@ -0,0 +1,100 @@
/* adapter_mxdatetime.h - definition for the mx date/time types
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_MXDATETIME_H
#define PSYCOPG_MXDATETIME_H 1
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject mxdatetimeType;
typedef struct {
PyObject HEAD;
PyObject *wrapped;
int type;
#define PSYCO_MXDATETIME_TIME 0
#define PSYCO_MXDATETIME_DATE 1
#define PSYCO_MXDATETIME_TIMESTAMP 2
#define PSYCO_MXDATETIME_INTERVAL 3
} mxdatetimeObject;
/* the conversion strings */
#define PSYCO_MXDATETIME_TIME_CONV "'%H:%M:%S'"
#define PSYCO_MXDATETIME_DATE_CONV "'%Y-%m-%d'"
#define PSYCO_MXDATETIME_TIMESTAMP_CONV "'%Y-%m-%d %H:%M:%S'"
#define PSYCO_MXDATETIME_INTERVAL_CONV "'%d:%H:%M:%S'"
/* functions exported to psycopgmodule.c */
#ifdef PSYCOPG_DEFAULT_MXDATETIME
extern PyObject *psyco_Date(PyObject *module, PyObject *args);
#define psyco_Date_doc \
"psycopg.Date(year, month, day) -> new date"
extern PyObject *psyco_Time(PyObject *module, PyObject *args);
#define psyco_Time_doc \
"psycopg.Time(hour, minutes, seconds) -> new time"
extern PyObject *psyco_Timestamp(PyObject *module, PyObject *args);
#define psyco_Timestamp_doc \
"psycopg.Time(year, month, day, hour, minutes, seconds) -> new timestamp"
extern PyObject *psyco_DateFromTicks(PyObject *module, PyObject *args);
#define psyco_DateFromTicks_doc \
"psycopg.DateFromTicks(ticks) -> new date"
extern PyObject *psyco_TimeFromTicks(PyObject *module, PyObject *args);
#define psyco_TimeFromTicks_doc \
"psycopg.TimeFromTicks(ticks) -> new time"
extern PyObject *psyco_TimestampFromTicks(PyObject *module, PyObject *args);
#define psyco_TimestampFromTicks_doc \
"psycopg.TimestampFromTicks(ticks) -> new timestamp"
#endif /* PSYCOPG_DEFAULT_MXDATETIME */
extern PyObject *psyco_DateFromMx(PyObject *module, PyObject *args);
#define psyco_DateFromMx_doc \
"psycopg.DateFromMx(mx) -> new date"
extern PyObject *psyco_TimeFromMx(PyObject *module, PyObject *args);
#define psyco_TimeFromMx_doc \
"psycopg.TimeFromMx(mx) -> new time"
extern PyObject *psyco_TimestampFromMx(PyObject *module, PyObject *args);
#define psyco_TimestampFromMx_doc \
"psycopg.TimestampFromMx(mx) -> new timestamp"
extern PyObject *psyco_IntervalFromMx(PyObject *module, PyObject *args);
#define psyco_IntervalFromMx_doc \
"psycopg.IntervalFromMx(mx) -> new interval"
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_MXDATETIME_H) */

223
psycopg/adapter_pboolean.c Normal file
View File

@ -0,0 +1,223 @@
/* adapter_pboolean.c - psycopg boolean type wrapper implementation
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/adapter_pboolean.h"
/** the Boolean object **/
static PyObject *
pboolean_str(pbooleanObject *self)
{
if (PyObject_IsTrue(self->wrapped)) {
return PyString_FromString("'t'");
}
else {
return PyString_FromString("'f'");
}
}
PyObject *
pboolean_getquoted(pbooleanObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
return pboolean_str(self);
}
PyObject *
pboolean_prepare(pbooleanObject *self, PyObject *args)
{
PyObject *fake;
if (!PyArg_ParseTuple(args, "O", &fake)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/** the Boolean object */
/* object member list */
static struct PyMemberDef pbooleanObject_members[] = {
{"adapted", T_OBJECT, offsetof(pbooleanObject, wrapped), RO},
{NULL}
};
/* object method table */
static PyMethodDef pbooleanObject_methods[] = {
{"getquoted", (PyCFunction)pboolean_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL-quoted string"},
{"prepare", (PyCFunction)pboolean_prepare, METH_VARARGS,
"prepare(conn) -> currently does nothing"},
{NULL} /* Sentinel */
};
/* initialization and finalization methods */
static int
pboolean_setup(pbooleanObject *self, PyObject *obj)
{
Dprintf("pboolean_setup: init pboolean object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
self->wrapped = obj;
Py_INCREF(self->wrapped);
Dprintf("pboolean_setup: good pboolean object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
return 0;
}
static void
pboolean_dealloc(PyObject* obj)
{
pbooleanObject *self = (pbooleanObject *)obj;
Py_XDECREF(self->wrapped);
Dprintf("pboolean_dealloc: deleted pboolean object at %p, refcnt = %d",
obj, obj->ob_refcnt);
obj->ob_type->tp_free(obj);
}
static int
pboolean_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *o;
if (!PyArg_ParseTuple(args, "O", &o))
return -1;
return pboolean_setup((pbooleanObject *)obj, o);
}
static PyObject *
pboolean_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
pboolean_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject *
pboolean_repr(pbooleanObject *self)
{
return PyString_FromFormat("<psycopg.Boolean object at %p>", self);
}
/* object type */
#define pbooleanType_doc \
"psycopg.Boolean(str) -> new Boolean adapter object"
PyTypeObject pbooleanType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg.Boolean",
sizeof(pbooleanObject),
0,
pboolean_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)pboolean_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)pboolean_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
pbooleanType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pbooleanObject_methods, /*tp_methods*/
pbooleanObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
pboolean_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
pboolean_new, /*tp_new*/
(freefunc)pboolean_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};
/** module-level functions **/
PyObject *
psyco_Boolean(PyObject *module, PyObject *args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O", &obj))
return NULL;
return PyObject_CallFunction((PyObject *)&pbooleanType, "O", obj);
}

View File

@ -0,0 +1,51 @@
/* adapter_pboolean.h - definition for the psycopg boolean type wrapper
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_PBOOLEAN_H
#define PSYCOPG_PBOOLEAN_H 1
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject pbooleanType;
typedef struct {
PyObject HEAD;
/* this is the real object we wrap */
PyObject *wrapped;
} pbooleanObject;
/* functions exported to psycopgmodule.c */
extern PyObject *psyco_Boolean(PyObject *module, PyObject *args);
#define psyco_Boolean_doc \
"psycopg.Boolean(obj) -> new boolean value"
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_PBOOLEAN_H) */

354
psycopg/adapter_qstring.c Normal file
View File

@ -0,0 +1,354 @@
/* adapter_qstring.c - QuotedString objects
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <libpq-fe.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/adapter_qstring.h"
/** the quoting code */
#ifndef PSYCOPG_OWN_QUOTING
#define qstring_escape PQescapeString
#else
static size_t
qstring_escape(char *to, char *from, size_t len)
{
int i, j;
for (i=0, j=0; i<len; i++) {
switch(from[i]) {
case '\'':
to[j++] = '\'';
to[j++] = '\'';
break;
case '\\':
to[j++] = '\\';
to[j++] = '\\';
break;
case '\0':
/* do nothing, embedded \0 are discarded */
break;
default:
to[j++] = from[i];
}
}
to[j] = '\0';
Dprintf("qstring_quote: to = %s", to);
return strlen(to);
}
#endif
/* qstring_quote - do the quote process on plain and unicode strings */
static PyObject *
qstring_quote(qstringObject *self)
{
PyObject *str;
char *s, *buffer;
size_t len;
/* if the wrapped object is an unicode object we can encode it to match
self->encoding but if the encoding is not specified we don't know what
to do and we raise an exception */
/* TODO: we need a real translation table from postgres encoding names to
python ones here */
if (PyUnicode_Check(self->wrapped) && self->encoding) {
PyObject *enc = PyDict_GetItemString(psycoEncodings, self->encoding);
/* note that pgenc is a borrowed reference */
if (enc) {
char *s = PyString_AsString(enc);
Dprintf("qstring_quote: encoding unicode object to %s", s);
str = PyUnicode_AsEncodedString(self->wrapped, s, NULL);
Dprintf("qstring_quote: got encoded object at %p", str);
if (str == NULL) return NULL;
}
else {
/* can't find the right encoder, raise exception */
PyErr_Format(InterfaceError,
"can't encode unicode string to %s", self->encoding);
return NULL;
}
}
/* if the wrapped object is a simple string, we don't know how to
(re)encode it, so we pass it as-is */
else if (PyString_Check(self->wrapped)) {
str = self->wrapped;
/* INCREF to make it ref-wise identical to unicode one */
Py_INCREF(str);
}
/* if the wrapped object is not a string, this is an error */
else {
PyErr_SetString(PyExc_TypeError,
"can't quote non-string object (or missing encoding)");
return NULL;
}
/* encode the string into buffer */
s = PyString_AsString(str);
len = strlen(s);
buffer = (char *)PyMem_Malloc((len*2+3) * sizeof(char));
if (buffer == NULL) {
Py_DECREF(str);
PyErr_NoMemory();
return NULL;
}
Py_BEGIN_ALLOW_THREADS;
len = qstring_escape(buffer+1, s, len);
buffer[0] = '\'' ; buffer[len+1] = '\'';
Py_END_ALLOW_THREADS;
self->buffer = PyString_FromStringAndSize(buffer, len+2);
PyMem_Free(buffer);
Py_DECREF(str);
return self->buffer;
}
/* qstring_str, qstring_getquoted - return result of quoting */
static PyObject *
qstring_str(qstringObject *self)
{
if (self->buffer == NULL) {
qstring_quote(self);
}
Py_XINCREF(self->buffer);
return self->buffer;
}
PyObject *
qstring_getquoted(qstringObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
return qstring_str(self);
}
PyObject *
qstring_prepare(qstringObject *self, PyObject *args)
{
connectionObject *conn;
if (!PyArg_ParseTuple(args, "O", &conn))
return NULL;
/* we bother copying the encoding only if the wrapped string is unicode,
we don't need the encoding if that's not the case */
if (PyUnicode_Check(self->wrapped)) {
if (self->encoding) free(self->encoding);
self->encoding = strdup(conn->encoding);
Dprintf("qstring_prepare: set encoding to %s", conn->encoding);
}
Py_INCREF(Py_None);
return Py_None;
}
/** the QuotedString object **/
/* object member list */
static struct PyMemberDef qstringObject_members[] = {
{"adapted", T_OBJECT, offsetof(qstringObject, wrapped), RO},
{"buffer", T_OBJECT, offsetof(qstringObject, buffer), RO},
{"encoding", T_STRING, offsetof(qstringObject, encoding), RO},
{NULL}
};
/* object method table */
static PyMethodDef qstringObject_methods[] = {
{"getquoted", (PyCFunction)qstring_getquoted, METH_VARARGS,
"getquoted() -> wrapped object value as SQL-quoted string"},
{"prepare", (PyCFunction)qstring_prepare, METH_VARARGS,
"prepare(conn) -> set encoding to conn->encoding"},
{NULL} /* Sentinel */
};
/* initialization and finalization methods */
static int
qstring_setup(qstringObject *self, PyObject *str, char *enc)
{
Dprintf("qstring_setup: init qstring object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
self->buffer = NULL;
/* FIXME: remove this orrible strdup */
if (enc) self->encoding = strdup(enc);
self->wrapped = str;
Py_INCREF(self->wrapped);
Dprintf("qstring_setup: good qstring object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
return 0;
}
static void
qstring_dealloc(PyObject* obj)
{
qstringObject *self = (qstringObject *)obj;
Py_XDECREF(self->wrapped);
Py_XDECREF(self->buffer);
if (self->encoding) free(self->encoding);
Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = %d",
obj, obj->ob_refcnt);
obj->ob_type->tp_free(obj);
}
static int
qstring_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *str;
char *enc = "latin-1"; /* default encoding as in Python */
if (!PyArg_ParseTuple(args, "O|s", &str, &enc))
return -1;
return qstring_setup((qstringObject *)obj, str, enc);
}
static PyObject *
qstring_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
qstring_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject *
qstring_repr(qstringObject *self)
{
return PyString_FromFormat("<psycopg.QuotedString object at %p>", self);
}
/* object type */
#define qstringType_doc \
"psycopg.QuotedString(str, enc) -> new quoted object with 'enc' encoding"
PyTypeObject qstringType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg.QuotedString",
sizeof(qstringObject),
0,
qstring_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)qstring_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)qstring_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
qstringType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
qstringObject_methods, /*tp_methods*/
qstringObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
qstring_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
qstring_new, /*tp_new*/
(freefunc)qstring_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};
/** module-level functions **/
PyObject *
psyco_QuotedString(PyObject *module, PyObject *args)
{
PyObject *str;
char *enc = "latin-1"; /* default encoding as in Python */
if (!PyArg_ParseTuple(args, "O|s", &str, &enc))
return NULL;
return PyObject_CallFunction((PyObject *)&qstringType, "Os", str, enc);
}

51
psycopg/adapter_qstring.h Normal file
View File

@ -0,0 +1,51 @@
/* adapter_qstring.h - definition for the QuotedString type
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_QSTRING_H
#define PSYCOPG_QSTRING_H 1
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject qstringType;
typedef struct {
PyObject HEAD;
PyObject *wrapped;
PyObject *buffer;
char *encoding;
} qstringObject;
/* functions exported to psycopgmodule.c */
extern PyObject *psyco_QuotedString(PyObject *module, PyObject *args);
#define psyco_QuotedString_doc \
"psycopg.QuotedString(str, enc) -> new quoted string"
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_QSTRING_H) */

96
psycopg/config.h Normal file
View File

@ -0,0 +1,96 @@
/* config.h - general config and Dprintf macro
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_CONFIG_H
#define PSYCOPG_CONFIG_H 1
/* replacement for asprintf() */
#ifndef HAVE_ASPRINTF
extern int asprintf(char **buffer, char *fmt, ...);
#endif
/* debug printf-like function */
#if defined( __GNUC__) && !defined(__APPLE__)
#ifdef PSYCOPG_DEBUG
#include <sys/types.h>
#include <unistd.h>
#define Dprintf(fmt, args...) \
fprintf(stderr, "[%d] " fmt "\n", getpid() , ## args)
#else
#define Dprintf(fmt, args...)
#endif
#else /* !__GNUC__ or __APPLE__ */
#ifdef PSYCOPG_DEBUG
#include <stdarg.h>
static void Dprintf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
}
#else
static void Dprintf(const char *fmt, ...) {}
#endif
#endif
/* win32 specific stuff */
#ifndef _WIN32
#include <pthread.h>
#else
#include <winsock2.h>
#define pthread_mutex_t HANDLE
#define pthread_condvar_t HANDLE
#define pthread_mutex_lock(object) WaitForSingleObject(object, INFINITE)
#define pthread_mutex_unlock(object) ReleaseMutex(object)
#define pthread_mutex_destroy(ref) (CloseHandle(ref))
/* convert pthread mutex to native mutex */
static int pthread_mutex_init(pthread_mutex_t *mutex, void* fake)
{
*mutex = CreateMutex(NULL, FALSE, NULL);
return 0;
}
/* to work around the fact that Windows does not have a gmtime_r function, or
a proper gmtime function */
static struct tm *gmtime_r(time_t *t, struct tm *tm)
{
tm = gmtime(t);
return tm;
}
/* remove the inline keyword, since it doesn't work unless C++ file */
#define inline
#endif
#if defined(__FreeBSD__) || defined(_WIN32)
/* what's this, we have no round function either? */
static double round(double num)
{
return (num >= 0) ? floor(num + 0.5) : ceil(num - 0.5);
}
#endif
/* postgresql < 7.4 does not have PQfreemem */
#ifndef HAVE_PQFREEMEM
#define PQfreemem free
#endif
#endif /* !defined(PSYCOPG_CONFIG_H) */

98
psycopg/connection.h Normal file
View File

@ -0,0 +1,98 @@
/* connection.h - definition for the psycopg connection type
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_CONNECTION_H
#define PSYCOPG_CONNECTION_H 1
#include <Python.h>
#include <libpq-fe.h>
#ifdef __cplusplus
extern "C" {
#endif
/* connection status */
#define CONN_STATUS_READY 1
#define CONN_STATUS_BEGIN 2
#define CONN_STATUS_SYNC 3
#define CONN_STATUS_ASYNC 4
extern PyTypeObject connectionType;
typedef struct {
PyObject HEAD;
PyObject *cursors; /* all cursors derived from this connection */
pthread_mutex_t lock; /* the global connection lock */
char *dsn; /* data source name */
char *critical; /* critical error on this connection */
char *encoding; /* current backend encoding */
long int closed; /* 2 means connection has been closed */
long int isolation_level; /* isolation level for this connection */
int status; /* status of the connection */
int protocol; /* protocol version */
PGconn *pgconn; /* the postgresql connection */
PyObject *async_cursor;
/* notice processing */
PyObject *notice_list;
PyObject *notice_filter;
/* notifies */
PyObject *notifies;
/* errors (DBAPI-2.0 extension) */
PyObject *exc_Error;
PyObject *exc_Warning;
PyObject *exc_InterfaceError;
PyObject *exc_DatabaseError;
PyObject *exc_InternalError;
PyObject *exc_OperationalError;
PyObject *exc_ProgrammingError;
PyObject *exc_IntegrityError;
PyObject *exc_DataError;
PyObject *exc_NotSupportedError;
} connectionObject;
/* C-callable functions in connection_int.c and connection_ext.c */
extern int conn_connect(connectionObject *self);
extern void conn_close(connectionObject *self);
extern int conn_commit(connectionObject *self);
extern int conn_rollback(connectionObject *self);
extern int conn_switch_isolation_level(connectionObject *self, int level);
extern int conn_set_client_encoding(connectionObject *self, char *enc);
/* exception-raising macros */
#define EXC_IF_CONN_CLOSED(self) if ((self)->closed > 0) { \
PyErr_SetString(InterfaceError, "connection already closed"); \
return NULL; }
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_CONNECTION_H) */

277
psycopg/connection_int.c Normal file
View File

@ -0,0 +1,277 @@
/* connection_int.c - code used by the connection object
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/pqpath.h"
/* conn_notice_callback - process notices */
void
conn_notice_callback(void *args, const char *message)
{
connectionObject *self = (connectionObject *)args;
Dprintf("conn_notice_callback: %s", message);
/* unfortunately the old protocl return COPY FROM errors only as notices,
so we need to filter them looking for such errors */
if (strncmp(message, "ERROR", 5) == 0)
pq_set_critical(self, message);
else
PyList_Append(self->notice_list, PyString_FromString(message));
}
/* conn_connect - execute a connection to the dataabase */
int
conn_connect(connectionObject *self)
{
PGconn *pgconn;
PGresult *pgres;
/* we need the initial date style to be ISO, for typecasters; if the user
later change it, she must know what she's doing... */
const char *datestyle = "SET DATESTYLE TO 'ISO'";
const char *encoding = "SHOW client_encoding";
Py_BEGIN_ALLOW_THREADS;
pgconn = PQconnectdb(self->dsn);
Py_END_ALLOW_THREADS;
Dprintf("conn_connect: new postgresql connection at %p", pgconn);
if (pgconn == NULL)
{
Dprintf("conn_connect: PQconnectdb(%s) FAILED", self->dsn);
PyErr_SetString(OperationalError, "PQconnectdb() failed");
return -1;
}
else if (PQstatus(pgconn) == CONNECTION_BAD)
{
Dprintf("conn_connect: PQconnectdb(%s) returned BAD", self->dsn);
PyErr_SetString(OperationalError, PQerrorMessage(pgconn));
PQfinish(pgconn);
return -1;
}
PQsetNoticeProcessor(pgconn, conn_notice_callback, (void*)self);
Py_BEGIN_ALLOW_THREADS;
pgres = PQexec(pgconn, datestyle);
Py_END_ALLOW_THREADS;
if (pgres == NULL || PQresultStatus(pgres) != PGRES_COMMAND_OK ) {
Dprintf("conn_connect: setting datestyle to iso FAILED");
PyErr_SetString(OperationalError, "can't set datestyle to ISO");
PQfinish(pgconn);
IFCLEARPGRES(pgres);
return -1;
}
CLEARPGRES(pgres);
Py_BEGIN_ALLOW_THREADS;
pgres = PQexec(pgconn, encoding);
Py_END_ALLOW_THREADS;
if (pgres == NULL || PQresultStatus(pgres) != PGRES_TUPLES_OK) {
Dprintf("conn_connect: fetching current client_encoding FAILED");
PyErr_SetString(OperationalError, "can't fetch client_encoding");
PQfinish(pgconn);
IFCLEARPGRES(pgres);
return -1;
}
self->encoding = strdup(PQgetvalue(pgres, 0, 0));
CLEARPGRES(pgres);
if (PQsetnonblocking(pgconn, 1) != 0) {
Dprintf("conn_connect: PQsetnonblocking() FAILED");
PyErr_SetString(OperationalError, "PQsetnonblocking() failed");
PQfinish(pgconn);
return -1;
}
#ifdef HAVE_PQPROTOCOL3
self->protocol = PQprotocolVersion(pgconn);
#else
self->protocol = 2;
#endif
Dprintf("conn_connect: using protocol %d", self->protocol);
self->pgconn = pgconn;
return 0;
}
/* conn_close - do anything needed to shut down the connection */
void
conn_close(connectionObject *self)
{
int len, i;
PyObject *t = NULL;
/* sets this connection as closed even for other threads; also note that
we need to check the value of pgconn, because we get called even when
the connection fails! */
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);
self->closed = 1;
/* execute a forced rollback on the connection (but don't check the
result, we're going to close the pq connection anyway */
if (self->pgconn) pq_abort(self);
/* orphans all the children cursors but do NOT destroy them (note that we
need to lock the connection before orphaning a cursor: we don't want to
remove a connection from a cursor executing a DB operation */
pthread_mutex_unlock(&self->lock);
Py_END_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);
len = PyList_Size(self->cursors);
Dprintf("conn_close: ophaning %d cursors", len);
for (i = len-1; i >= 0; i--) {
t = PySequence_GetItem(self->cursors, i);
Dprintf("conn close: cursor at %p: refcnt = %d", t, t->ob_refcnt);
PySequence_DelItem(self->cursors, i);
((cursorObject *)t)->conn = NULL; /* orphaned */
Dprintf("conn_close: -> new refcnt = %d", t->ob_refcnt);
}
pthread_mutex_unlock(&self->lock);
/* now that all cursors have been orphaned (they can't operate on the
database anymore) we can shut down the connection */
if (self->pgconn) {
PQfinish(self->pgconn);
Dprintf("conn_close: PQfinish called");
self->pgconn = NULL;
}
}
/* conn_commit - commit on a connection */
int
conn_commit(connectionObject *self)
{
int res;
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);
res = pq_commit(self);
pthread_mutex_unlock(&self->lock);
Py_END_ALLOW_THREADS;
return res;
}
/* conn_rollback - rollback a connection */
int
conn_rollback(connectionObject *self)
{
int res;
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);
res = pq_abort(self);
pthread_mutex_unlock(&self->lock);
Py_END_ALLOW_THREADS;
return res;
}
/* conn_switch_isolation_level - switch isolation level on the connection */
int
conn_switch_isolation_level(connectionObject *self, int level)
{
int res = 0;
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);
/* if the current isolation level is > 0 we need to abort the current
transaction before changing; that all folks! */
if (self->isolation_level != level && self->isolation_level > 0) {
res = pq_abort(self);
}
self->isolation_level = level;
Dprintf("conn_switch_isolation_level: switched to level %d", level);
pthread_mutex_unlock(&self->lock);
Py_END_ALLOW_THREADS;
return res;
}
/* conn_set_client_encoding - switch client encoding on connection */
int
conn_set_client_encoding(connectionObject *self, char *enc)
{
PGresult *pgres;
char query[48];
int res = 0;
/* TODO: check for async query here and raise error if necessary */
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock);
/* set encoding, no encoding string is longer than 24 bytes */
snprintf(query, 47, "SET client_encoding = '%s'", enc);
/* abort the current transaction, to set the encoding ouside of
transactions */
res = pq_abort(self);
if (res == 0) {
pgres = PQexec(self->pgconn, query);
if (pgres == NULL || PQresultStatus(pgres) != PGRES_COMMAND_OK ) {
PyErr_Format(OperationalError, "can't set encoding to '%s'", enc);
res = -1;
}
IFCLEARPGRES(pgres);
if (self->encoding) free(self->encoding);
self->encoding = strdup(enc);
}
Dprintf("conn_set_client_encoding: set encoding to %s", self->encoding);
pthread_mutex_unlock(&self->lock);
Py_END_ALLOW_THREADS;
return res;
}

401
psycopg/connection_type.c Normal file
View File

@ -0,0 +1,401 @@
/* connection_type.c - python interface to connection objects
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
/** DBAPI methods **/
/* cursor method - allocate a new cursor */
#define psyco_conn_cursor_doc \
"cursor(factory=psycopg.cursor) -> new cursor\n\n" \
"Return a new cursor. The 'factory' argument can be used to create\n" \
"non-standard cursors by passing a class different from the default.\n" \
"Note that the new class *should* be a sub-class of psycopg.cursor."
static PyObject *
psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds)
{
char *name = NULL;
PyObject *obj, *factory = NULL;
static char *kwlist[] = {"name", "factory", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sO", kwlist,
&name, &factory)) {
return NULL;
}
EXC_IF_CONN_CLOSED(self);
Dprintf("psyco_conn_cursor: new cursor for connection at %p", self);
Dprintf("psyco_conn_cursor: parameters: name = %s", name);
if (factory == NULL) factory = (PyObject *)&cursorType;
obj = PyObject_CallFunction(factory, "O", self);
/* TODO: added error checking on obj (cursor) here */
/* add the cursor to this connection's list (and decref it, so that it has
the right number of references to go away even if still in the list) */
PyList_Append(self->cursors, obj);
Py_DECREF(obj);
Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = %d",
obj, obj->ob_refcnt);
return obj;
}
/* close method - close the connection and all related cursors */
#define psyco_conn_close_doc "close() -> close the connection"
static PyObject *
psyco_conn_close(connectionObject *self, PyObject *args)
{
EXC_IF_CONN_CLOSED(self);
if (!PyArg_ParseTuple(args, "")) return NULL;
Dprintf("psyco_conn_close: closing connection at %p", self);
conn_close(self);
Dprintf("psyco_conn_close: connection at %p closed", self);
Py_INCREF(Py_None);
return Py_None;
}
/* commit method - commit all changes to the database */
#define psyco_conn_commit_doc "commit() -> commit all changes to database"
static PyObject *
psyco_conn_commit(connectionObject *self, PyObject *args)
{
EXC_IF_CONN_CLOSED(self);
if (!PyArg_ParseTuple(args, "")) return NULL;
/* FIXME: check return status? */
conn_commit(self);
Py_INCREF(Py_None);
return Py_None;
}
/* rollback method - roll back all changes done to the database */
#define psyco_conn_rollback_doc \
"rollback() -> roll back all changes done to database"
static PyObject *
psyco_conn_rollback(connectionObject *self, PyObject *args)
{
EXC_IF_CONN_CLOSED(self);
if (!PyArg_ParseTuple(args, "")) return NULL;
/* FIXME: check return status? */
conn_rollback(self);
Py_INCREF(Py_None);
return Py_None;
}
/* set_isolation_level method - switch connection isolation level */
#define psyco_conn_set_isolation_level_doc \
"set_isolation_level(level) -> swicth isolation level to 'level'"
static PyObject *
psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
{
int level = 1;
EXC_IF_CONN_CLOSED(self);
if (!PyArg_ParseTuple(args, "i", &level)) return NULL;
if (level < 0 || level > 3) {
PyErr_SetString(PyExc_ValueError,
"isolation level out of bounds (0,3)");
return NULL;
}
/* FIXME: check return status? */
conn_switch_isolation_level(self, level);
Py_INCREF(Py_None);
return Py_None;
}
/* set_isolation_level method - switch connection isolation level */
#define psyco_conn_set_client_encoding_doc \
"set_client_encoding(level) -> swicth isolation level to 'level'"
static PyObject *
psyco_conn_set_client_encoding(connectionObject *self, PyObject *args)
{
char *enc = NULL;
EXC_IF_CONN_CLOSED(self);
if (!PyArg_ParseTuple(args, "s", &enc)) return NULL;
if (conn_set_client_encoding(self, enc) == 0) {
Py_INCREF(Py_None);
return Py_None;
}
else {
return NULL;
}
}
/** the connection object **/
/* object method list */
static struct PyMethodDef connectionObject_methods[] = {
{"cursor", (PyCFunction)psyco_conn_cursor,
METH_VARARGS|METH_KEYWORDS, psyco_conn_cursor_doc},
{"close", (PyCFunction)psyco_conn_close,
METH_VARARGS, psyco_conn_close_doc},
{"commit", (PyCFunction)psyco_conn_commit,
METH_VARARGS, psyco_conn_commit_doc},
{"rollback", (PyCFunction)psyco_conn_rollback,
METH_VARARGS, psyco_conn_rollback_doc},
#ifdef PSYCOPG_EXTENSIONS
{"set_isolation_level", (PyCFunction)psyco_conn_set_isolation_level,
METH_VARARGS, psyco_conn_set_isolation_level_doc},
{"set_client_encoding", (PyCFunction)psyco_conn_set_client_encoding,
METH_VARARGS, psyco_conn_set_client_encoding_doc},
#endif
{NULL}
};
/* object member list */
static struct PyMemberDef connectionObject_members[] = {
/* DBAPI-2.0 extensions (exception objects) */
{"Error", T_OBJECT, offsetof(connectionObject,exc_Error), RO},
{"Warning", T_OBJECT, offsetof(connectionObject, exc_Warning), RO},
{"InterfaceError", T_OBJECT,
offsetof(connectionObject, exc_InterfaceError), RO},
{"DatabaseError", T_OBJECT,
offsetof(connectionObject, exc_DatabaseError), RO},
{"InternalError", T_OBJECT,
offsetof(connectionObject, exc_InternalError), RO},
{"OperationalError", T_OBJECT,
offsetof(connectionObject, exc_OperationalError), RO},
{"ProgrammingError", T_OBJECT,
offsetof(connectionObject, exc_ProgrammingError), RO},
{"IntegrityError", T_OBJECT,
offsetof(connectionObject, exc_IntegrityError), RO},
{"DataError", T_OBJECT,
offsetof(connectionObject, exc_DataError), RO},
{"NotSupportedError", T_OBJECT,
offsetof(connectionObject, exc_NotSupportedError), RO},
#ifdef PSYCOPG_EXTENSIONS
{"closed", T_LONG, offsetof(connectionObject, closed), RO},
{"isolation_level", T_LONG,
offsetof(connectionObject, isolation_level), RO},
{"encoding", T_STRING, offsetof(connectionObject, encoding), RO},
{"cursors", T_OBJECT, offsetof(connectionObject, cursors), RO},
{"notices", T_OBJECT, offsetof(connectionObject, notice_list), RO},
{"notifies", T_OBJECT, offsetof(connectionObject, notifies), RO},
{"dsn", T_STRING, offsetof(connectionObject, dsn), RO},
#endif
{NULL}
};
/* initialization and finalization methods */
static int
connection_setup(connectionObject *self, char *dsn)
{
Dprintf("connection_setup: init connection object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
self->dsn = strdup(dsn);
self->cursors = PyList_New(0);
self->notice_list = PyList_New(0);
self->closed = 0;
self->isolation_level = 1;
self->status = CONN_STATUS_READY;
self->critical = NULL;
self->async_cursor = NULL;
self->pgconn = NULL;
pthread_mutex_init(&(self->lock), NULL);
if (conn_connect(self) != 0) {
Py_XDECREF(self->cursors);
pthread_mutex_destroy(&(self->lock));
Dprintf("connection_init: FAILED");
return -1;
}
Dprintf("connection_setup: good connection object at %p, refcnt = %d",
self, ((PyObject *)self)->ob_refcnt);
return 0;
}
static void
connection_dealloc(PyObject* obj)
{
connectionObject *self = (connectionObject *)obj;
if (self->closed == 0) conn_close(self);
Py_XDECREF(self->cursors);
if (self->dsn) free(self->dsn);
if (self->encoding) free(self->encoding);
if (self->critical) free(self->critical);
pthread_mutex_destroy(&(self->lock));
Dprintf("connection_dealloc: deleted connection object at %p, refcnt = %d",
obj, obj->ob_refcnt);
obj->ob_type->tp_free(obj);
}
static int
connection_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
char *dsn;
if (!PyArg_ParseTuple(args, "s", &dsn))
return -1;
return connection_setup((connectionObject *)obj, dsn);
}
static PyObject *
connection_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
connection_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject *
connection_repr(connectionObject *self)
{
return PyString_FromFormat(
"<connection object at %p; dsn: '%s', closed: %ld>",
self, self->dsn, self->closed);
}
/* object type */
#define connectionType_doc \
"connection(dsn, ...) -> new connection object"
PyTypeObject connectionType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg.connection",
sizeof(connectionObject),
0,
connection_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)connection_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)connection_repr, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
connectionType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
connectionObject_methods, /*tp_methods*/
connectionObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
connection_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
connection_new, /*tp_new*/
(freefunc)connection_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};

87
psycopg/cursor.h Normal file
View File

@ -0,0 +1,87 @@
/* cursor.h - definition for the psycopg cursor type
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_CURSOR_H
#define PSYCOPG_CURSOR_H 1
#include <Python.h>
#include <libpq-fe.h>
#include "psycopg/connection.h"
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject cursorType;
typedef struct {
PyObject HEAD;
connectionObject *conn; /* connection owning the cursor */
int closed:1; /* 1 if the cursor is closed */
int notuples:1; /* 1 if the command was not a SELECT query */
long int rowcount; /* number of rows affected by last execute */
long int columns; /* number of columns fetched from the db */
long int arraysize; /* how many rows should fetchmany() return */
long int row; /* the row counter for fetch*() operations */
PyObject *description; /* read-only attribute: sequence of 7-item
sequences.*/
/* postgres connection stuff */
PGresult *pgres; /* result of last query */
PyObject *pgstatus; /* last message from the server after an execute */
Oid lastoid; /* last oid from an insert or InvalidOid */
PyObject *casts; /* an array (tuple) of typecast functions */
PyObject *copyfile; /* file-like used during COPY TO/FROM ops */
long int copysize; /* size of the copy buffer during COPY TO/FROM ops */
#define DEFAULT_COPYSIZE 16384
PyObject *tuple_factory; /* factory for result tuples */
PyObject *tzinfo_factory; /* factory for tzinfo objects */
char *qattr; /* quoting attr, used when quoting strings */
char *notice; /* a notice from the backend */
char *query; /* last query executed */
} cursorObject;
/* C-callable functions in cursor_int.c and cursor_ext.c */
extern void curs_reset(cursorObject *self);
/* exception-raising macros */
#define EXC_IF_CURS_CLOSED(self) if ((self)->closed) { \
PyErr_SetString(InterfaceError, "cursor already closed"); \
return NULL; }
#define EXC_IF_NO_TUPLES(self) if ((self)->notuples) { \
PyErr_SetString(ProgrammingError, "no results to fetch"); \
return NULL; }
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_CURSOR_H) */

47
psycopg/cursor_int.c Normal file
View File

@ -0,0 +1,47 @@
/* cursor_int.c - code used by the cursor object
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/psycopg.h"
#include "psycopg/cursor.h"
#include "psycopg/pqpath.h"
/* curs_reset - reset the cursor to a clean state */
void
curs_reset(cursorObject *self)
{
/* initialize some variables to default values */
self->notuples = 1;
self->rowcount = -1;
self->row = 0;
Py_XDECREF(self->description);
Py_INCREF(Py_None);
self->description = Py_None;
Py_XDECREF(self->casts);
self->casts = NULL;
}

1204
psycopg/cursor_type.c Normal file

File diff suppressed because it is too large Load Diff

121
psycopg/microprotocols.c Normal file
View File

@ -0,0 +1,121 @@
/* microprotocols.c - minimalist and non-validating protocols implementation
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/cursor.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
/** the adapters registry **/
PyObject *psyco_adapters;
/* microprotocols_init - initialize the adapters dictionary */
int
microprotocols_init(PyObject *dict)
{
/* create adapters dictionary and put it in module namespace */
if ((psyco_adapters = PyDict_New()) == NULL) {
return -1;
}
PyDict_SetItemString(dict, "adapters", psyco_adapters);
return 0;
}
/* microprotocols_add - add a reverse type-caster to the dictionary */
int
microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
{
if (proto == NULL) proto = (PyObject*)&isqlquoteType;
Dprintf("microprotocols_add: cast %p for (%s, ?)",
cast, type->tp_name);
PyDict_SetItem(psyco_adapters,
Py_BuildValue("(OO)", (PyObject*)type, proto),
cast);
return 0;
}
/* microprotocols_adapt - adapt an object to the built-in protocol */
PyObject *
microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
{
PyObject *adapter, *key;
/* we don't check for exact type conformance as specified in PEP 246
because the ISQLQuote type is abstract and there is no way to get a
quotable object to be its instance */
/* look for an adapter in the registry */
key = Py_BuildValue("(OO)", (PyObject*)obj->ob_type, proto);
adapter = PyDict_GetItem(psyco_adapters, key);
Py_DECREF(key);
if (adapter) {
PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
return adapted;
}
/* try to have the protocol adapt this object*/
if (PyObject_HasAttrString(proto, "__adapt__")) {
PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj);
if (adapted && adapted != Py_None) return adapted;
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
return NULL;
}
/* and finally try to have the object adapt itself */
if (PyObject_HasAttrString(obj, "__conform__")) {
PyObject *adapted = PyObject_CallMethod(proto, "__conform__","O", obj);
if (adapted && adapted != Py_None) return adapted;
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
return NULL;
}
/* else set the right exception and return NULL */
PyErr_SetString(ProgrammingError, "can't adapt");
return NULL;
}
/** module-level functions **/
PyObject *
psyco_microprotocols_adapt(cursorObject *self, PyObject *args)
{
PyObject *obj, *alt = NULL;
PyObject *proto = (PyObject*)&isqlquoteType;
if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL;
return microprotocols_adapt(obj, proto, alt);
}

55
psycopg/microprotocols.h Normal file
View File

@ -0,0 +1,55 @@
/* microprotocols.c - definitions for minimalist and non-validating protocols
*
* Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_MICROPROTOCOLS_H
#define PSYCOPG_MICROPROTOCOLS_H 1
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
/** adapters registry **/
extern PyObject *psyco_adapters;
/** the names of the three mandatory methods **/
#define MICROPROTOCOLS_GETQUOTED_NAME "getquoted"
#define MICROPROTOCOLS_GETSTRING_NAME "getstring"
#define MICROPROTOCOLS_GETBINARY_NAME "getbinary"
/** exported functions **/
/* used by module.c to init the microprotocols system */
extern int microprotocols_init(PyObject *dict);
extern int microprotocols_add(
PyTypeObject *type, PyObject *proto, PyObject *cast);
extern PyObject *microprotocols_adapt(
PyObject *obj, PyObject *proto, PyObject *alt);
extern PyObject *
psyco_microprotocols_adapt(cursorObject *self, PyObject *args);
#define psyco_microprotocols_adapt_doc \
"psycopg.adapt(obj, protocol, alternate) -> adapt obj to given protocol"
#endif /* !defined(PSYCOPG_MICROPROTOCOLS_H) */

View File

@ -0,0 +1,211 @@
/* microprotocol_proto.c - psycopg protocols
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/microprotocols_proto.h"
/** void protocol implementation **/
/* getquoted - return quoted representation for object */
#define psyco_isqlquote_getquoted_doc \
"getquoted() -> return SQL-quoted representation of this object"
static PyObject *
psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/* getbinary - return quoted representation for object */
#define psyco_isqlquote_getbinary_doc \
"getbinary() -> return SQL-quoted binary representation of this object"
static PyObject *
psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/* getbuffer - return quoted representation for object */
#define psyco_isqlquote_getbuffer_doc \
"getbuffer() -> return this object"
static PyObject *
psyco_isqlquote_getbuffer(isqlquoteObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "")) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/** the ISQLQuote object **/
/* object method list */
static struct PyMethodDef isqlquoteObject_methods[] = {
{"getquoted", (PyCFunction)psyco_isqlquote_getquoted,
METH_VARARGS, psyco_isqlquote_getquoted_doc},
{"getbinary", (PyCFunction)psyco_isqlquote_getbinary,
METH_VARARGS, psyco_isqlquote_getbinary_doc},
{"getbuffer", (PyCFunction)psyco_isqlquote_getbuffer,
METH_VARARGS, psyco_isqlquote_getbuffer_doc},
{NULL}
};
/* object member list */
static struct PyMemberDef isqlquoteObject_members[] = {
/* DBAPI-2.0 extensions (exception objects) */
{"_wrapped", T_OBJECT, offsetof(isqlquoteObject, wrapped), RO},
{NULL}
};
/* initialization and finalization methods */
static int
isqlquote_setup(isqlquoteObject *self, PyObject *wrapped)
{
self->wrapped = wrapped;
Py_INCREF(wrapped);
return 0;
}
static void
isqlquote_dealloc(PyObject* obj)
{
isqlquoteObject *self = (isqlquoteObject *)obj;
Py_XDECREF(self->wrapped);
obj->ob_type->tp_free(obj);
}
static int
isqlquote_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *wrapped = NULL;
if (!PyArg_ParseTuple(args, "O", &wrapped))
return -1;
return isqlquote_setup((isqlquoteObject *)obj, wrapped);
}
static PyObject *
isqlquote_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static void
isqlquote_del(PyObject* self)
{
PyObject_Del(self);
}
/* object type */
#define isqlquoteType_doc \
"Abstract ISQLQuote protocol"
PyTypeObject isqlquoteType = {
PyObject_HEAD_INIT(NULL)
0,
"psycopg.ISQLQuote",
sizeof(isqlquoteObject),
0,
isqlquote_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
isqlquoteType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
isqlquoteObject_methods, /*tp_methods*/
isqlquoteObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
isqlquote_init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
isqlquote_new, /*tp_new*/
(freefunc)isqlquote_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};

View File

@ -0,0 +1,45 @@
/* microporotocols_proto.h - definiton for psycopg's protocols
*
* Copyright (C) 2004 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_ISQLQUOTE_H
#define PSYCOPG_ISQLQUOTE_H 1
#include <Python.h>
#include <libpq-fe.h>
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject isqlquoteType;
typedef struct {
PyObject HEAD;
PyObject *wrapped;
} isqlquoteObject;
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_ISQLQUOTE_H) */

60
psycopg/pgtypes.h Normal file
View File

@ -0,0 +1,60 @@
#define BOOLOID 16
#define BYTEAOID 17
#define CHAROID 18
#define NAMEOID 19
#define INT8OID 20
#define INT2OID 21
#define INT2VECTOROID 22
#define INT4OID 23
#define REGPROCOID 24
#define TEXTOID 25
#define OIDOID 26
#define TIDOID 27
#define XIDOID 28
#define CIDOID 29
#define OIDVECTOROID 30
#define POINTOID 600
#define LSEGOID 601
#define PATHOID 602
#define BOXOID 603
#define POLYGONOID 604
#define LINEOID 628
#define FLOAT4OID 700
#define FLOAT8OID 701
#define ABSTIMEOID 702
#define RELTIMEOID 703
#define TINTERVALOID 704
#define UNKNOWNOID 705
#define CIRCLEOID 718
#define CASHOID 790
#define MACADDROID 829
#define INETOID 869
#define CIDROID 650
#define ACLITEMOID 1033
#define BPCHAROID 1042
#define VARCHAROID 1043
#define DATEOID 1082
#define TIMEOID 1083
#define TIMESTAMPOID 1114
#define TIMESTAMPTZOID 1184
#define INTERVALOID 1186
#define TIMETZOID 1266
#define BITOID 1560
#define VARBITOID 1562
#define NUMERICOID 1700
#define REFCURSOROID 1790
#define REGPROCEDUREOID 2202
#define REGOPEROID 2203
#define REGOPERATOROID 2204
#define REGCLASSOID 2205
#define REGTYPEOID 2206
#define RECORDOID 2249
#define CSTRINGOID 2275
#define ANYOID 2276
#define ANYARRAYOID 2277
#define VOIDOID 2278
#define TRIGGEROID 2279
#define LANGUAGE_HANDLEROID 2280
#define INTERNALOID 2281
#define OPAQUEOID 2282
#define ANYELEMENTOID 2283

2
psycopg/pgversion.h Normal file
View File

@ -0,0 +1,2 @@
#define PG_VERSION_MAJOR 7
#define PG_VERSION_MINOR 4

777
psycopg/pqpath.c Normal file
View File

@ -0,0 +1,777 @@
/* pqpath.c - single path into libpq
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/pqpath.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/typecast.h"
#include "psycopg/pgtypes.h"
#include "psycopg/pgversion.h"
/* pq_raise - raise a python exception of the right kind */
void
pq_raise(connectionObject *conn, cursorObject *curs, PyObject *exc, char *msg)
{
char *err = NULL;
if ((conn == NULL && curs == NULL) || (curs != NULL && conn == NULL)) {
PyErr_SetString(Error,
"psycopg went psycotic and raised a null error");
return;
}
if (curs && curs->pgres)
err = PQresultErrorMessage(curs->pgres);
if (err == NULL)
err = PQerrorMessage(conn->pgconn);
/* if the is no error message we probably called pq_raise without reason:
we need to set an exception anyway because the caller will probably
raise and a meaningful message is better than an empty one */
if (err == NULL) {
PyErr_SetString(Error, "psycopg went psycotic without error set");
return;
}
/* if exc is NULL, analyze the message and try to deduce the right
exception kind (only if we have a pgres, obviously) */
if (exc == NULL) {
if (curs && curs->pgres) {
if (conn->protocol == 3) {
#ifdef HAVE_PQPROTOCOL3
char *pgstate = PQresultErrorField(curs->pgres,
PG_DIAG_SQLSTATE);
if (!strncmp(pgstate, "23", 2))
exc = IntegrityError;
else
exc = ProgrammingError;
#endif
}
}
}
/* if exc is still NULL psycopg was not built with HAVE_PQPROTOCOL3 or the
connection is using protocol 2: in both cases we default to comparing
error messages */
if (exc == NULL) {
if (!strncmp(err, "ERROR: Cannot insert a duplicate key", 37)
|| !strncmp(err, "ERROR: ExecAppend: Fail to add null", 36)
|| strstr(err, "referential integrity violation"))
exc = IntegrityError;
else
exc = ProgrammingError;
}
/* try to remove the initial "ERROR: " part from the postgresql error */
if (err && strlen(err) > 8) err = &(err[8]);
/* if msg is not NULL, add it to the error message, after a '\n' */
if (msg) {
PyErr_Format(exc, "%s\n%s", err, msg);
}
else {
PyErr_SetString(exc, err);
}
}
/* pq_set_critical, pq_resolve_critical - manage critical errors
this function is invoked when a PQexec() call returns NULL, meaning a
critical condition like out of memory or lost connection. it save the error
message and mark the connection as 'wanting cleanup'.
both functions do not call any Py_*_ALLOW_THREADS macros. */
void
pq_set_critical(connectionObject *conn, const char *msg)
{
if (msg == NULL)
msg = PQerrorMessage(conn->pgconn);
if (conn->critical) free(conn->critical);
if (msg && msg[0] != '\0') conn->critical = strdup(msg);
else conn->critical = NULL;
}
PyObject *
pq_resolve_critical(connectionObject *conn, int close)
{
if (conn->critical) {
char *msg = &(conn->critical[6]);
Dprintf("pq_resolve_critical: error = %s", msg);
/* we can't use pq_raise because the error has already been cleared
from the connection, so we just raise an OperationalError with the
critical message */
PyErr_SetString(OperationalError, msg);
/* we don't want to destroy this connection but just close it */
if (close == 1) conn_close(conn);
}
return NULL;
}
/* pq_clear_async - clear the effects of a previous async query
note that this function does block because it needs to wait for the full
result sets of the previous query to clear them.
this function does not call any Py_*_ALLOW_THREADS macros */
void
pq_clear_async(connectionObject *conn)
{
PGresult *pgres;
do {
pgres = PQgetResult(conn->pgconn);
IFCLEARPGRES(pgres);
} while (pgres != NULL);
}
/* pq_begin - send a BEGIN WORK, if necessary
this function does not call any Py_*_ALLOW_THREADS macros */
int
pq_begin(connectionObject *conn)
{
const char *query[] = {
NULL,
"BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED",
"BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE",
"BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"};
int pgstatus, retvalue = -1;
PGresult *pgres = NULL;
Dprintf("pq_begin: pgconn = %p, isolevel = %ld, status = %d",
conn->pgconn, conn->isolation_level, conn->status);
if (conn->isolation_level == 0 || conn->status != CONN_STATUS_READY) {
Dprintf("pq_begin: transaction in progress");
return 0;
}
pq_clear_async(conn);
pgres = PQexec(conn->pgconn, query[conn->isolation_level]);
if (pgres == NULL) {
Dprintf("pq_begin: PQexec() failed");
pq_set_critical(conn, NULL);
goto cleanup;
}
pgstatus = PQresultStatus(pgres);
if (pgstatus != PGRES_COMMAND_OK ) {
Dprintf("pq_begin: result is NOT OK");
pq_set_critical(conn, NULL);
goto cleanup;
}
Dprintf("pq_begin: issued '%s' command", query[conn->isolation_level]);
retvalue = 0;
conn->status = CONN_STATUS_BEGIN;
cleanup:
IFCLEARPGRES(pgres);
return retvalue;
}
/* pq_commit - send an END, if necessary
this function does not call any Py_*_ALLOW_THREADS macros */
int
pq_commit(connectionObject *conn)
{
const char *query = "END";
int pgstatus, retvalue = -1;
PGresult *pgres = NULL;
Dprintf("pq_commit: pgconn = %p, isolevel = %ld, status = %d",
conn->pgconn, conn->isolation_level, conn->status);
if (conn->isolation_level == 0 || conn->status != CONN_STATUS_BEGIN) {
Dprintf("pq_commit: no transaction to commit");
return 0;
}
pq_clear_async(conn);
pgres = PQexec(conn->pgconn, query);
if (pgres == NULL) {
Dprintf("pq_commit: PQexec() failed");
pq_set_critical(conn, NULL);
goto cleanup;
}
pgstatus = PQresultStatus(pgres);
if (pgstatus != PGRES_COMMAND_OK ) {
Dprintf("pq_commit: result is NOT OK");
pq_set_critical(conn, NULL);
goto cleanup;
}
Dprintf("pq_commit: issued '%s' command", query);
retvalue = 0;
conn->status = CONN_STATUS_READY;
cleanup:
IFCLEARPGRES(pgres);
return retvalue;
}
/* pq_abort - send an ABORT, if necessary
this function does not call any Py_*_ALLOW_THREADS macros */
int
pq_abort(connectionObject *conn)
{
const char *query = "ABORT";
int pgstatus, retvalue = -1;
PGresult *pgres = NULL;
Dprintf("pq_abort: pgconn = %p, isolevel = %ld, status = %d",
conn->pgconn, conn->isolation_level, conn->status);
if (conn->isolation_level == 0 || conn->status != CONN_STATUS_BEGIN) {
Dprintf("pq_abort: no transaction to abort");
return 0;
}
pq_clear_async(conn);
pgres = PQexec(conn->pgconn, query);
if (pgres == NULL) {
Dprintf("pq_abort: PQexec() failed");
pq_set_critical(conn, NULL);
goto cleanup;
}
pgstatus = PQresultStatus(pgres);
if (pgstatus != PGRES_COMMAND_OK ) {
Dprintf("pq_abort: result is NOT OK");
pq_set_critical(conn, NULL);
goto cleanup;
}
Dprintf("pq_abort: issued '%s' command", query);
retvalue = 0;
conn->status = CONN_STATUS_READY;
cleanup:
IFCLEARPGRES(pgres);
return retvalue;
}
/* pq_is_busy - consume input and return connection status
a status of 1 means that a call to pq_fetch will block, while a status of 0
means that there is data available to be collected. -1 means an error, the
exception will be set accordingly. */
int
pq_is_busy(connectionObject *conn)
{
PGnotify *pgn;
Dprintf("pq_is_busy: consuming input");
if (PQconsumeInput(conn->pgconn) == 0) {
Dprintf("pq_is_busy: PQconsumeInput() failed");
PyErr_SetString(OperationalError, PQerrorMessage(conn->pgconn));
return -1;
}
/* now check for notifies */
while ((pgn = PQnotifies(conn->pgconn)) != NULL) {
PyObject *notify;
Dprintf("curs_is_busy: got NOTIFY from pid %d, msg = %s",
pgn->be_pid, pgn->relname);
notify = PyTuple_New(2);
PyTuple_SET_ITEM(notify, 0, PyInt_FromLong((long)pgn->be_pid));
PyTuple_SET_ITEM(notify, 1, PyString_FromString(pgn->relname));
pthread_mutex_lock(&(conn->lock));
PyList_Append(conn->notifies, notify);
pthread_mutex_unlock(&(conn->lock));
free(pgn);
}
return PQisBusy(conn->pgconn);
}
/* pq_execute - execute a query, possibly asyncronously
this fucntion locks the connection object
this function call Py_*_ALLOW_THREADS macros */
int
pq_execute(cursorObject *curs, const char *query, int async)
{
int err;
/* if the status of the connection is critical raise an exception and
definitely close the connection */
if (curs->conn->critical) {
pq_resolve_critical(curs->conn, 1);
return -1;
}
/* check status of connection, raise error if not OK */
if (PQstatus(curs->conn->pgconn) != CONNECTION_OK) {
Dprintf("pq_execute: connection NOT OK");
PyErr_SetString(OperationalError, PQerrorMessage(curs->conn->pgconn));
return -1;
}
Dprintf("curs_execute: pg connection at %p OK", curs->conn->pgconn);
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&(curs->conn->lock));
pq_begin(curs->conn);
if (async == 0) {
IFCLEARPGRES(curs->pgres);
Dprintf("pq_execute: executing SYNC query:");
Dprintf(" %-.200s", query);
curs->pgres = PQexec(curs->conn->pgconn, query);
}
else if (async == 1) {
/* first of all, let see if the previous query has already ended, if
not what should we do? just block and discard data or execute
another query? */
pq_clear_async(curs->conn);
Dprintf("pq_execute: executing ASYNC query:");
Dprintf(" %-.200s", query);
/* then we can go on and send a new query without fear */
IFCLEARPGRES(curs->pgres);
if (PQsendQuery(curs->conn->pgconn, query) == 0) {
pthread_mutex_unlock(&(curs->conn->lock));
Py_BLOCK_THREADS;
PyErr_SetString(OperationalError,
PQerrorMessage(curs->conn->pgconn));
return -1;
}
Dprintf("pq_execute: async query sent to backend");
}
pthread_mutex_unlock(&(curs->conn->lock));
Py_END_ALLOW_THREADS;
/* if the execute was sync, we call pq_fetch() immediately,
to respect the old DBAPI-2.0 compatible behaviour */
if (async == 0) {
Dprintf("pq_execute: entering syncronous DBAPI compatibility mode");
do {
err = pq_fetch(curs);
if (err == -1) return -1;
} while (err == 1);
}
else {
curs->conn->async_cursor = (PyObject*)curs;
}
return 1-async;
}
/* pq_fetch - fetch data after a query
this fucntion locks the connection object
this function call Py_*_ALLOW_THREADS macros
return value:
-1 - some error occurred while calling libpq
0 - no result from the backend but no libpq errors
1 - result from backend (possibly data is ready)
*/
static void
_pq_fetch_tuples(cursorObject *curs)
{
int i, *dsize = NULL;
int pgnfields = PQnfields(curs->pgres);
int pgbintuples = PQbinaryTuples(curs->pgres);
curs->notuples = 0;
/* create the tuple for description and typecasting */
Py_XDECREF(curs->description);
Py_XDECREF(curs->casts);
curs->description = PyTuple_New(pgnfields);
curs->casts = PyTuple_New(pgnfields);
curs->columns = pgnfields;
/* calculate the display size for each column (cpu intensive, can be
switched off at configuration time) */
#ifdef PSYCOPG_DISPLAY_SIZE
dsize = (int *)calloc(pgnfields, sizeof(int));
if (dsize != NULL) {
if (curs->rowcount == 0) {
for (i=0; i < pgnfields; i++)
dsize[i] = -1;
}
else {
int j, len;
for (j = 0; j < curs->rowcount; j++) {
for (i = 0; i < pgnfields; i++) {
len = PQgetlength(curs->pgres, j, i);
if (len > dsize[i]) dsize[i] = len;
}
}
}
}
#endif
/* calculate various parameters and typecasters */
for (i = 0; i < pgnfields; i++) {
Oid ftype = PQftype(curs->pgres, i);
int fsize = PQfsize(curs->pgres, i);
int fmod = PQfmod(curs->pgres, i);
PyObject *dtitem = PyTuple_New(7);
PyObject *type = PyInt_FromLong(ftype);
PyObject *cast;
PyTuple_SET_ITEM(curs->description, i, dtitem);
/* fill the right cast function by accessing the global dictionary of
casting objects. if we got no defined cast use the default
one */
if (!(cast = PyDict_GetItem(curs->casts, type))) {
Dprintf("_pq_fetch_tuples: cast %d not in per-cursor dict", ftype);
if (!(cast = PyDict_GetItem(psyco_types, type))) {
Dprintf("_pq_fetch_tuples: cast %d not found, using default",
PQftype(curs->pgres,i));
cast = psyco_default_cast;
}
}
/* else if we got binary tuples and if we got a field that
is binary use the default cast.
*/
else if (pgbintuples && cast == psyco_default_binary_cast) {
Dprintf("_pq_fetch_tuples: Binary cursor and "
"binary field: %i using default cast",
PQftype(curs->pgres,i));
cast = psyco_default_cast;
}
Dprintf("_pq_fetch_tuples: using cast at %p (%s) for type %d",
cast, PyString_AS_STRING(((typecastObject*)cast)->name),
PQftype(curs->pgres,i));
Py_INCREF(cast);
PyTuple_SET_ITEM(curs->casts, i, cast);
/* 1/ fill the other fields */
PyTuple_SET_ITEM(dtitem, 0,
PyString_FromString(PQfname(curs->pgres, i)));
PyTuple_SET_ITEM(dtitem, 1, type);
/* 2/ display size is the maximum size of this field result tuples. */
if (dsize && dsize[i] >= 0) {
PyTuple_SET_ITEM(dtitem, 2, PyInt_FromLong(dsize[i]));
}
else {
Py_INCREF(Py_None);
PyTuple_SET_ITEM(dtitem, 2, Py_None);
}
/* 3/ size on the backend */
if (fmod > 0) fmod = fmod - sizeof(int);
if (fsize == -1) {
if (ftype == NUMERICOID) {
PyTuple_SET_ITEM(dtitem, 3,
PyInt_FromLong((fmod >> 16) & 0xFFFF));
}
else { /* If variable length record, return maximum size */
PyTuple_SET_ITEM(dtitem, 3, PyInt_FromLong(fmod));
}
}
else {
PyTuple_SET_ITEM(dtitem, 3, PyInt_FromLong(fsize));
}
/* 4,5/ scale and precision */
if (ftype == NUMERICOID) {
PyTuple_SET_ITEM(dtitem, 4, PyInt_FromLong((fmod >> 16) & 0xFFFF));
PyTuple_SET_ITEM(dtitem, 5, PyInt_FromLong(fmod & 0xFFFF));
}
else {
Py_INCREF(Py_None);
PyTuple_SET_ITEM(dtitem, 4, Py_None);
Py_INCREF(Py_None);
PyTuple_SET_ITEM(dtitem, 5, Py_None);
}
/* 6/ FIXME: null_ok??? */
Py_INCREF(Py_None);
PyTuple_SET_ITEM(dtitem, 6, Py_None);
}
if (dsize) free(dsize);
}
#ifdef HAVE_PQPROTOCOL3
static int
_pq_copy_in(cursorObject *curs)
{
/* COPY FROM implementation when protocol 3 is available: this function
uses the new PQputCopyData() and can detect errors and set the correct
exception */
return -1;
}
#else
static int
_pq_copy_in(cursorObject *curs)
{
/* COPY FROM implementation when protocol 3 is not available: this
function can't fail but the backend will send an ERROR notice that will
be catched by our notice collector */
PyObject *o;
while (1) {
o = PyObject_CallMethod(curs->copyfile, "readline", NULL);
if (!o || o == Py_None || PyString_GET_SIZE(o) == 0) break;
if (PQputline(curs->conn->pgconn, PyString_AS_STRING(o)) != 0) {
Py_DECREF(o);
return -1;
}
Py_DECREF(o);
}
Py_XDECREF(o);
PQputline(curs->conn->pgconn, "\\.\n");
PQendcopy(curs->conn->pgconn);
return 1;
}
#endif
#ifdef HAVE_PQPROTOCOL3
static int
_pq_copy_out(cursorObject *curs)
{
char *buffer;
int len;
while (1) {
Py_BEGIN_ALLOW_THREADS;
len = PQgetCopyData(curs->conn->pgconn, &buffer, 0);
Py_END_ALLOW_THREADS;
if (len > 0 && buffer) {
PyObject_CallMethod(curs->copyfile, "write", "s", buffer);
PQfreemem(buffer);
}
/* we break on len == 0 but note that that should *not* happen,
because we are not doing an async call (if it happens blame
postgresql authors :/) */
else if (len <= 0) break;
}
if (len == -2) {
pq_raise(curs->conn, NULL, NULL, NULL);
return -1;
}
return 1;
}
#else
static int
_pq_copy_out(cursorObject *curs)
{
char buffer[4096];
int status, len;
PyObject *o;
while (1) {
Py_BEGIN_ALLOW_THREADS;
status = PQgetline(curs->conn->pgconn, buffer, 4096);
if (status == 0) {
if (buffer[0] == '\\' && buffer[1] == '.') break;
len = strlen(buffer);
buffer[len++] = '\n';
}
else if (status == 1) {
len = 4096-1;
}
else {
Py_BLOCK_THREADS;
return -1;
}
Py_END_ALLOW_THREADS;
o = PyString_FromStringAndSize(buffer, len);
PyObject_CallMethod(curs->copyfile, "write", "O", o);
Py_DECREF(o);
}
if (PQendcopy(curs->conn->pgconn) != 0) return -1;
return 1;
}
#endif
int
pq_fetch(cursorObject *curs)
{
int pgstatus, ex = -1;
/* even if we fail, we remove any information about the previous query */
curs_reset(curs);
/* we check the result from the previous execute; if the result is not
already there, we need to consume some input and go to sleep until we
get something edible to eat */
if (!curs->pgres) {
Dprintf("pq_fetch: no data: entering polling loop");
Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&(curs->conn->lock));
while (pq_is_busy(curs->conn) > 0) {
fd_set rfds;
struct timeval tv;
int sval, sock;
sock = PQsocket(curs->conn->pgconn);
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
/* set a default timeout of 5 seconds
TODO: make use of the timeout, maybe allowing the user to
make a non-blocking (timeouted) call to fetchXXX */
tv.tv_sec = 5;
tv.tv_usec = 0;
Dprintf("pq_fetch: entering PDflush() loop");
while (PQflush(curs->conn->pgconn) != 0);
sval = select(sock+1, &rfds, NULL, NULL, &tv);
}
Dprintf("pq_fetch: data is probably ready");
IFCLEARPGRES(curs->pgres);
curs->pgres = PQgetResult(curs->conn->pgconn);
pthread_mutex_unlock(&(curs->conn->lock));
Py_END_ALLOW_THREADS;
}
/* check for PGRES_FATAL_ERROR result */
/* FIXME: I am not sure we need to check for critical error here.
if (curs->pgres == NULL) {
Dprintf("pq_fetch: got a NULL pgres, checking for critical");
pq_set_critical(curs->conn);
if (curs->conn->critical) {
pq_resolve_critical(curs->conn);
return -1;
}
else {
return 0;
}
}
*/
if (curs->pgres == NULL) return 0;
pgstatus = PQresultStatus(curs->pgres);
Dprintf("pq_fetch: pgstatus = %s", PQresStatus(pgstatus));
/* backend status message */
Py_XDECREF(curs->pgstatus);
curs->pgstatus = PyString_FromString(PQcmdStatus(curs->pgres));
/* rowcount has a meaning even for INSERT and UPDATES but to get the right
number we need to check two times, one with PQntuples for SELECts and
one with PQcmdTuples for other queries */
curs->rowcount = PQntuples(curs->pgres);
if (curs->rowcount == 0)
curs->rowcount = atoi(PQcmdTuples(curs->pgres));
switch(pgstatus) {
case PGRES_COMMAND_OK:
Dprintf("pq_fetch: command returned OK (no tuples)");
curs->rowcount = 0;
curs->lastoid = PQoidValue(curs->pgres);
CLEARPGRES(curs->pgres);
ex = 1;
break;
case PGRES_COPY_OUT:
Dprintf("pq_fetch: data from a COPY TO (no tuples)");
curs->rowcount = 0;
ex = _pq_copy_out(curs);
/* error caught by out glorious notice handler */
if (PyErr_Occurred()) ex = -1;
IFCLEARPGRES(curs->pgres);
break;
case PGRES_COPY_IN:
Dprintf("pq_fetch: data from a COPY FROM (no tuples)");
curs->rowcount = 0;
ex = _pq_copy_in(curs);
/* error caught by out glorious notice handler */
if (PyErr_Occurred()) ex = -1;
IFCLEARPGRES(curs->pgres);
break;
case PGRES_TUPLES_OK:
Dprintf("pq_fetch: data from a SELECT (got tuples)");
_pq_fetch_tuples(curs); ex = 0;
/* don't clear curs->pgres, because it contains the results! */
break;
default:
Dprintf("pq_fetch: uh-oh, something FAILED");
pq_raise(curs->conn, curs, NULL, NULL);
IFCLEARPGRES(curs->pgres);
ex = -1;
break;
}
/* error checking, close the connection if necessary (some critical errors
are not really critical, like a COPY FROM error: if that's the case we
raise the exception but we avoid to close the connection) */
if (curs->conn->critical) {
if (ex == -1) {
pq_resolve_critical(curs->conn, 1);
}
else {
pq_resolve_critical(curs->conn, 0);
}
return -1;
}
return ex;
}

41
psycopg/pqpath.h Normal file
View File

@ -0,0 +1,41 @@
/* pqpath.h - definitions for pqpath.c
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_PQPATH_H
#define PSYCOPG_PQPATH_H 1
#include "psycopg/cursor.h"
#include "psycopg/connection.h"
/* macros to clean the pg result */
#define IFCLEARPGRES(pgres) if (pgres) {PQclear(pgres); pgres = NULL;}
#define CLEARPGRES(pgres) PQclear(pgres); pgres = NULL
/* exported functions */
extern int pq_fetch(cursorObject *curs);
extern int pq_execute(cursorObject *curs, const char *query, int async);
extern int pq_begin(connectionObject *conn);
extern int pq_commit(connectionObject *conn);
extern int pq_abort(connectionObject *conn);
extern int pq_is_busy(connectionObject *conn);
extern void pq_set_critical(connectionObject *conn, const char *msg);
#endif /* !defined(PSYCOPG_PQPATH_H) */

107
psycopg/psycopg.h Normal file
View File

@ -0,0 +1,107 @@
/* psycopg.h - definitions for the psycopg python module
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_H
#define PSYCOPG_H 1
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
/* DBAPI compliance parameters */
#define APILEVEL "2.0"
#define THREADSAFETY 2
#define PARAMSTYLE "pyformat"
/* C API functions */
#define psyco_errors_fill_NUM 0
#define psyco_errors_fill_RETURN void
#define psyco_errors_fill_PROTO (PyObject *dict)
#define psyco_errors_set_NUM 1
#define psyco_errors_set_RETURN void
#define psyco_errors_set_PROTO (PyObject *type)
/* Total number of C API pointers */
#define PSYCOPG_API_pointers 2
#ifdef PSYCOPG_MODULE
/** This section is used when compiling psycopgmodule.c & co. **/
extern psyco_errors_fill_RETURN psyco_errors_fill psyco_errors_fill_PROTO;
extern psyco_errors_set_RETURN psyco_errors_set psyco_errors_set_PROTO;
/* global excpetions */
extern PyObject *Error, *Warning, *InterfaceError, *DatabaseError,
*InternalError, *OperationalError, *ProgrammingError,
*IntegrityError, *DataError, *NotSupportedError;
/* python versions and compatibility stuff */
#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC void
#endif
#else
/** This section is used in modules that use psycopg's C API **/
static void **PSYCOPG_API;
#define psyco_errors_fill \
(*(psyco_errors_fill_RETURN (*)psyco_errors_fill_PROTO) \
PSYCOPG_API[psyco_errors_fill_NUM])
#define psyco_errors_set \
(*(psyco_errors_set_RETURN (*)psyco_errors_set_PROTO) \
PSYCOPG_API[psyco_errors_set_NUM])
/* Return -1 and set exception on error, 0 on success. */
static int
import_psycopg(void)
{
PyObject *module = PyImport_ImportModule("psycopg");
if (module != NULL) {
PyObject *c_api_object = PyObject_GetAttrString(module, "_C_API");
if (c_api_object == NULL) return -1;
if (PyCObject_Check(c_api_object))
PSYCOPG_API = (void **)PyCObject_AsVoidPtr(c_api_object);
Py_DECREF(c_api_object);
}
return 0;
}
#endif
/* postgresql<->python encoding map */
extern PyObject *psycoEncodings;
typedef struct {
char *pgenc;
char *pyenc;
} encodingPair;
/* the Decimal type, used by the DECIMAL typecaster */
extern PyObject *decimalType;
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_H) */

477
psycopg/psycopgmodule.c Normal file
View File

@ -0,0 +1,477 @@
/* psycopgmodule.c - psycopg module (will import other C classes)
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/python.h"
#include "psycopg/psycopg.h"
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/typecast.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
#include "psycopg/adapter_qstring.h"
#include "psycopg/adapter_binary.h"
#include "psycopg/adapter_pboolean.h"
#ifdef HAVE_MXDATETIME
#include <mxDateTime.h>
#include "psycopg/adapter_mxdatetime.h"
mxDateTimeModule_APIObject *mxDateTimeP = NULL;
#endif
/* some module-level variables, like the datetime module */
#ifdef HAVE_PYDATETIME
#include <datetime.h>
#include "psycopg/adapter_datetime.h"
PyObject *pyDateTimeModuleP = NULL;
PyObject *pyDateTypeP = NULL;
PyObject *pyTimeTypeP = NULL;
PyObject *pyDateTimeTypeP = NULL;
PyObject *pyDeltaTypeP = NULL;
#endif
PyObject *psycoEncodings = NULL;
PyObject *decimalType = NULL;
/** connect module-level function **/
#define psyco_connect_doc "connect(dsn, ...) -> new connection object"
static int
_psyco_connect_fill_dsn(char *dsn, char *kw, char *v, int i)
{
strcpy(&dsn[i], kw); i += strlen(kw);
strcpy(&dsn[i], v); i += strlen(v);
return i;
}
static void
_psyco_connect_fill_exc(connectionObject *conn)
{
/* fill the connection object with the exceptions */
conn->exc_Error = Error;
Py_INCREF(Error);
conn->exc_Warning = Warning;
Py_INCREF(Warning);
conn->exc_InterfaceError = Error;
Py_INCREF(InterfaceError);
conn->exc_DatabaseError = Error;
Py_INCREF(DatabaseError);
conn->exc_InternalError = Error;
Py_INCREF(InternalError);
conn->exc_ProgrammingError = Error;
Py_INCREF(ProgrammingError);
conn->exc_IntegrityError = Error;
Py_INCREF(IntegrityError);
conn->exc_DataError = Error;
Py_INCREF(DataError);
conn->exc_NotSupportedError = NotSupportedError;
Py_INCREF(NotSupportedError);
}
static PyObject *
psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
{
PyObject *conn, *factory = NULL;
int idsn=-1;
char *dsn=NULL, *database=NULL, *user=NULL, *password=NULL;
char *host=NULL, *port=NULL, *sslmode=NULL;
static char *kwlist[] = {"dsn", "database", "host", "port",
"user", "password", "sslmode", "factory", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sssssssO", kwlist,
&dsn, &database, &host, &port,
&user, &password, &sslmode, &factory)) {
return NULL;
}
if (dsn == NULL) {
int l = 36; /* len("dbname= user= password= host= port=\0") */
if (database) l += strlen(database);
if (host) l += strlen(host);
if (port) l += strlen(port);
if (user) l += strlen(user);
if (password) l += strlen(password);
if (sslmode) l += strlen(sslmode);
dsn = malloc(l*sizeof(char));
if (dsn == NULL) {
PyErr_SetString(InterfaceError, "dynamic dsn allocation failed");
return NULL;
}
idsn = 0;
if (database)
idsn = _psyco_connect_fill_dsn(dsn, " dbname=", database, idsn);
if (host)
idsn = _psyco_connect_fill_dsn(dsn, " host=", host, idsn);
if (port)
idsn = _psyco_connect_fill_dsn(dsn, " port=", port, idsn);
if (user)
idsn = _psyco_connect_fill_dsn(dsn, " user=", user, idsn);
if (password)
idsn = _psyco_connect_fill_dsn(dsn, " password=", password, idsn);
if (sslmode)
idsn = _psyco_connect_fill_dsn(dsn, " sslmode=", sslmode, idsn);
if (idsn > 0) {
dsn[idsn] = '\0';
memmove(dsn, &dsn[1], idsn);
}
else {
free(dsn);
PyErr_SetString(InterfaceError, "missing dsn and no parameters");
return NULL;
}
}
Dprintf("psyco_connect: dsn = '%s'", dsn);
/* allocate connection, fill with errors and return it */
if (factory == NULL) factory = (PyObject *)&connectionType;
conn = PyObject_CallFunction(factory, "s", dsn);
if (conn) _psyco_connect_fill_exc((connectionObject*)conn);
return conn;
}
/** type registration **/
#define psyco_register_type_doc \
"register_type(obj) -> register obj with psycopg type system"
static PyObject *
psyco_register_type(PyObject *self, PyObject *args)
{
PyObject *type;
if (!PyArg_ParseTuple(args, "O!", &typecastType, &type)) {
return NULL;
}
typecast_add(type, 0);
Py_INCREF(Py_None);
return Py_None;
}
/* default adapters */
static void
psyco_adapters_init(PyObject *mod)
{
PyObject *call;
microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType);
microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType);
microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType);
#ifdef HAVE_MXDATETIME
/* the module has already been initialized, so we can obtain the callable
objects directly from its dictionary :) */
call = PyMapping_GetItemString(mod, "TimestampFromMx");
microprotocols_add(mxDateTimeP->DateTime_Type, NULL, call);
call = PyMapping_GetItemString(mod, "TimeFromMx");
microprotocols_add(mxDateTimeP->DateTimeDelta_Type, NULL, call);
#endif
#ifdef HAVE_PYDATETIME
/* as above, we use the callable objects from the psycopg module */
call = PyMapping_GetItemString(mod, "DateFromPy");
microprotocols_add((PyTypeObject*)pyDateTypeP, NULL, call);
call = PyMapping_GetItemString(mod, "TimeFromPy");
microprotocols_add((PyTypeObject*)pyTimeTypeP, NULL, call);
call = PyMapping_GetItemString(mod, "TimestampFromPy");
microprotocols_add((PyTypeObject*)pyDateTimeTypeP, NULL, call);
call = PyMapping_GetItemString(mod, "IntervalFromPy");
microprotocols_add((PyTypeObject*)pyDeltaTypeP, NULL, call);
#endif
#ifdef HAVE_PYBOOL
microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType);
#endif
}
/* psyco_encodings_fill
Fill the module's postgresql<->python encoding table */
static encodingPair encodings[] = {
{"SQL_ASCII", "ascii"},
{"LATIN1", "latin_1"},
{"UNICODE", "utf_8"},
{NULL, NULL}
};
static void psyco_encodings_fill(PyObject *dict)
{
encodingPair *enc;
for (enc = encodings; enc->pgenc != NULL; enc++) {
PyObject *value = PyString_FromString(enc->pyenc);
PyDict_SetItemString(dict, enc->pgenc, value);
Py_DECREF(value);
}
}
/* psyco_errors_init, psyco_errors_fill (callable from C)
Initialize the module's exceptions and after that a dictionary with a full
set of exceptions. */
PyObject *Error, *Warning, *InterfaceError, *DatabaseError,
*InternalError, *OperationalError, *ProgrammingError,
*IntegrityError, *DataError, *NotSupportedError;
static void
psyco_errors_init(void)
{
Error = PyErr_NewException("psycopg.Error", PyExc_StandardError, NULL);
Warning = PyErr_NewException("psycopg.Warning", PyExc_StandardError,NULL);
InterfaceError = PyErr_NewException("psycopg.InterfaceError", Error, NULL);
DatabaseError = PyErr_NewException("psycopg.DatabaseError", Error, NULL);
InternalError =
PyErr_NewException("psycopg.InternalError", DatabaseError, NULL);
OperationalError =
PyErr_NewException("psycopg.OperationalError", DatabaseError, NULL);
ProgrammingError =
PyErr_NewException("psycopg.ProgrammingError", DatabaseError, NULL);
IntegrityError =
PyErr_NewException("psycopg.IntegrityError", DatabaseError,NULL);
DataError =
PyErr_NewException("psycopg.DataError", DatabaseError, NULL);
NotSupportedError =
PyErr_NewException("psycopg.NotSupportedError", DatabaseError, NULL);
}
void
psyco_errors_fill(PyObject *dict)
{
PyDict_SetItemString(dict, "Error", Error);
PyDict_SetItemString(dict, "Warning", Warning);
PyDict_SetItemString(dict, "InterfaceError", InterfaceError);
PyDict_SetItemString(dict, "DatabaseError", DatabaseError);
PyDict_SetItemString(dict, "InternalError", InternalError);
PyDict_SetItemString(dict, "OperationalError", OperationalError);
PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError);
PyDict_SetItemString(dict, "IntegrityError", IntegrityError);
PyDict_SetItemString(dict, "DataError", DataError);
PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError);
}
void
psyco_errors_set(PyObject *type)
{
PyObject_SetAttrString(type, "Error", Error);
PyObject_SetAttrString(type, "Warning", Warning);
PyObject_SetAttrString(type, "InterfaceError", InterfaceError);
PyObject_SetAttrString(type, "DatabaseError", DatabaseError);
PyObject_SetAttrString(type, "InternalError", InternalError);
PyObject_SetAttrString(type, "OperationalError", OperationalError);
PyObject_SetAttrString(type, "ProgrammingError", ProgrammingError);
PyObject_SetAttrString(type, "IntegrityError", IntegrityError);
PyObject_SetAttrString(type, "DataError", DataError);
PyObject_SetAttrString(type, "NotSupportedError", NotSupportedError);
}
/* psyco_decimal_init
Initialize the module's pointer to the decimal type. */
void
psyco_decimal_init(void)
{
#ifdef HAVE_DECIMAL
PyObject *decimal = PyImport_ImportModule("decimal");
if (decimal) {
decimalType = PyObject_GetAttrString(decimal, "Decimal");
}
#endif
}
/** method table and module initialization **/
static PyMethodDef psycopgMethods[] = {
{"connect", (PyCFunction)psyco_connect,
METH_VARARGS|METH_KEYWORDS, psyco_connect_doc},
{"adapt", (PyCFunction)psyco_microprotocols_adapt,
METH_VARARGS, psyco_microprotocols_adapt_doc},
{"register_type", (PyCFunction)psyco_register_type,
METH_VARARGS, psyco_register_type_doc},
{"new_type", (PyCFunction)typecast_from_python,
METH_VARARGS|METH_KEYWORDS},
{"QuotedString", (PyCFunction)psyco_QuotedString,
METH_VARARGS, psyco_QuotedString_doc},
{"Boolean", (PyCFunction)psyco_Boolean,
METH_VARARGS, psyco_Boolean_doc},
{"Binary", (PyCFunction)psyco_Binary,
METH_VARARGS, psyco_Binary_doc},
{"Date", (PyCFunction)psyco_Date,
METH_VARARGS, psyco_Date_doc},
{"Time", (PyCFunction)psyco_Time,
METH_VARARGS, psyco_Time_doc},
{"Timestamp", (PyCFunction)psyco_Timestamp,
METH_VARARGS, psyco_Timestamp_doc},
{"DateFromTicks", (PyCFunction)psyco_DateFromTicks,
METH_VARARGS, psyco_DateFromTicks_doc},
{"TimeFromTicks", (PyCFunction)psyco_TimeFromTicks,
METH_VARARGS, psyco_TimeFromTicks_doc},
{"TimestampFromTicks", (PyCFunction)psyco_TimestampFromTicks,
METH_VARARGS, psyco_TimestampFromTicks_doc},
#ifdef HAVE_MXDATETIME
{"DateFromMx", (PyCFunction)psyco_DateFromMx,
METH_VARARGS, psyco_DateFromMx_doc},
{"TimeFromMx", (PyCFunction)psyco_TimeFromMx,
METH_VARARGS, psyco_TimeFromMx_doc},
{"TimestampFromMx", (PyCFunction)psyco_TimestampFromMx,
METH_VARARGS, psyco_TimestampFromMx_doc},
{"IntervalFromMx", (PyCFunction)psyco_IntervalFromMx,
METH_VARARGS, psyco_IntervalFromMx_doc},
#endif
#ifdef HAVE_PYDATETIME
{"DateFromPy", (PyCFunction)psyco_DateFromPy,
METH_VARARGS, psyco_DateFromPy_doc},
{"TimeFromPy", (PyCFunction)psyco_TimeFromPy,
METH_VARARGS, psyco_TimeFromPy_doc},
{"TimestampFromPy", (PyCFunction)psyco_TimestampFromPy,
METH_VARARGS, psyco_TimestampFromPy_doc},
{"IntervalFromPy", (PyCFunction)psyco_IntervalFromPy,
METH_VARARGS, psyco_IntervalFromPy_doc},
#endif
{NULL, NULL, 0, NULL} /* Sentinel */
};
PyMODINIT_FUNC
init_psycopg(void)
{
static void *PSYCOPG_API[PSYCOPG_API_pointers];
PyObject *module, *dict;
PyObject *c_api_object;
Dprintf("initpsycopg: initializing psycopg %s", PSYCOPG_VERSION);
/* initialize all the new types and then the module */
connectionType.ob_type = &PyType_Type;
cursorType.ob_type = &PyType_Type;
typecastType.ob_type = &PyType_Type;
qstringType.ob_type = &PyType_Type;
binaryType.ob_type = &PyType_Type;
isqlquoteType.ob_type = &PyType_Type;
if (PyType_Ready(&connectionType) == -1) return;
if (PyType_Ready(&cursorType) == -1) return;
if (PyType_Ready(&typecastType) == -1) return;
if (PyType_Ready(&qstringType) == -1) return;
if (PyType_Ready(&binaryType) == -1) return;
if (PyType_Ready(&isqlquoteType) == -1) return;
#ifdef HAVE_PYBOOL
pbooleanType.ob_type = &PyType_Type;
if (PyType_Ready(&pbooleanType) == -1) return;
#endif
/* import mx.DateTime module, if necessary */
#ifdef HAVE_MXDATETIME
mxdatetimeType.ob_type = &PyType_Type;
if (PyType_Ready(&mxdatetimeType) == -1) return;
if (mxDateTime_ImportModuleAndAPI() != 0) {
Dprintf("initpsycopg: why marc hide mx.DateTime again?!");
PyErr_SetString(PyExc_ImportError, "can't import mx.DateTime module");
return;
}
mxDateTimeP = &mxDateTime;
#endif
/* import python builtin datetime module, if available */
#ifdef HAVE_PYDATETIME
pyDateTimeModuleP = PyImport_ImportModule("datetime");
pydatetimeType.ob_type = &PyType_Type;
if (PyType_Ready(&pydatetimeType) == -1) return;
/* now we define the datetime types, this is crazy because python should
be doing that, not us! */
pyDateTypeP = PyObject_GetAttrString(pyDateTimeModuleP, "date");
pyTimeTypeP = PyObject_GetAttrString(pyDateTimeModuleP, "time");
pyDateTimeTypeP = PyObject_GetAttrString(pyDateTimeModuleP, "datetime");
pyDeltaTypeP = PyObject_GetAttrString(pyDateTimeModuleP, "timedelta");
#endif
/* initialize the module and grab module's dictionary */
module = Py_InitModule("_psycopg", psycopgMethods);
dict = PyModule_GetDict(module);
/* initialize all the module's exported functions */
/* PyBoxer_API[PyBoxer_Fake_NUM] = (void *)PyBoxer_Fake; */
/* Create a CObject containing the API pointer array's address */
c_api_object = PyCObject_FromVoidPtr((void *)PSYCOPG_API, NULL);
if (c_api_object != NULL)
PyModule_AddObject(module, "_C_API", c_api_object);
/* other mixed initializations of module-level variables */
psycoEncodings = PyDict_New();
psyco_encodings_fill(psycoEncodings);
psyco_decimal_init();
/* set some module's parameters */
PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION);
PyModule_AddStringConstant(module, "__doc__", "psycopg PostgreSQL driver");
PyModule_AddObject(module, "apilevel", PyString_FromString(APILEVEL));
PyModule_AddObject(module, "threadsafety", PyInt_FromLong(THREADSAFETY));
PyModule_AddObject(module, "paramstyle", PyString_FromString(PARAMSTYLE));
/* put new types in module dictionary */
PyModule_AddObject(module, "connection", (PyObject*)&connectionType);
PyModule_AddObject(module, "cursor", (PyObject*)&cursorType);
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
/* encodings dictionary in module dictionary */
PyModule_AddObject(module, "encodings", psycoEncodings);
/* initialize default set of typecasters */
typecast_init(dict);
/* initialize microprotocols layer */
microprotocols_init(dict);
psyco_adapters_init(dict);
/* create a standard set of exceptions and add them to the module's dict */
psyco_errors_init();
psyco_errors_fill(dict);
Dprintf("initpsycopg: module initialization complete");
}

43
psycopg/python.h Normal file
View File

@ -0,0 +1,43 @@
/* python.h - python version compatibility stuff
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_PYTHON_H
#define PSYCOPG_PYTHON_H 1
#include <Python.h>
#include <structmember.h>
/* python < 2.2 does not have PyMemeberDef */
#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 2
#define PyMemberDef memberlist
#endif
/* PyObject_TypeCheck introduced in 2.2 */
#ifndef PyObject_TypeCheck
#define PyObject_TypeCheck(o, t) ((o)->ob_type == (t))
#endif
/* python 2.2 does not have freefunc (it has destructor instead) */
#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 3
#define freefunc destructor
#endif
#endif /* !defined(PSYCOPG_PYTHON_H) */

439
psycopg/typecast.c Normal file
View File

@ -0,0 +1,439 @@
/* typecast.c - basic utility functions related to typecasting
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Python.h>
#include <structmember.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
#include "psycopg/psycopg.h"
#include "psycopg/python.h"
#include "psycopg/typecast.h"
#include "psycopg/cursor.h"
/* usefull function used by some typecasters */
static char *
skip_until_space(char *s)
{
while (*s && *s != ' ') s++;
return s;
}
/** include casting objects **/
#include "psycopg/typecast_basic.c"
#ifdef HAVE_MXDATETIME
#include "psycopg/typecast_mxdatetime.c"
#endif
#ifdef HAVE_PYDATETIME
#include "psycopg/typecast_datetime.c"
#endif
#include "psycopg/typecast_builtins.c"
/* a list of initializers, used to make the typecasters accessible anyway */
#ifdef HAVE_PYDATETIME
typecastObject_initlist typecast_pydatetime[] = {
{"PYDATETIME", typecast_DATETIME_types, typecast_PYDATETIME_cast},
{"PYTIME", typecast_TIME_types, typecast_PYTIME_cast},
{"PYDATE", typecast_DATE_types, typecast_PYDATE_cast},
{"PYINTERVAL", typecast_INTERVAL_types, typecast_PYINTERVAL_cast},
{NULL, NULL, NULL}
};
#endif
/* a list of initializers, used to make the typecasters accessible anyway */
#ifdef HAVE_MXDATETIME
typecastObject_initlist typecast_mxdatetime[] = {
{"MXDATETIME", typecast_DATETIME_types, typecast_MXDATE_cast},
{"MXTIME", typecast_TIME_types, typecast_MXTIME_cast},
{"MXDATE", typecast_DATE_types, typecast_MXDATE_cast},
{"MXINTERVAL", typecast_INTERVAL_types, typecast_MXINTERVAL_cast},
{NULL, NULL, NULL}
};
#endif
/** the type dictionary and associated functions **/
PyObject *psyco_types;
PyObject *psyco_default_cast;
PyObject *psyco_binary_types;
PyObject *psyco_default_binary_cast;
static long int typecast_default_DEFAULT[] = {0};
static typecastObject_initlist typecast_default = {
"DEFAULT", typecast_default_DEFAULT, typecast_STRING_cast};
/* typecast_init - initialize the dictionary and create default types */
int
typecast_init(PyObject *dict)
{
int i;
/* create type dictionary and put it in module namespace */
psyco_types = PyDict_New();
psyco_binary_types = PyDict_New();
if (!psyco_types || !psyco_binary_types) {
Py_XDECREF(psyco_types);
Py_XDECREF(psyco_binary_types);
return -1;
}
PyDict_SetItemString(dict, "string_types", psyco_types);
PyDict_SetItemString(dict, "binary_types", psyco_binary_types);
/* insert the cast types into the 'types' dictionary and register them in
the module dictionary */
for (i = 0; typecast_builtins[i].name != NULL; i++) {
typecastObject *t;
Dprintf("typecast_init: initializing %s", typecast_builtins[i].name);
t = (typecastObject *)typecast_from_c(&(typecast_builtins[i]));
if (t == NULL) return -1;
if (typecast_add((PyObject *)t, 0) != 0) return -1;
PyDict_SetItem(dict, t->name, (PyObject *)t);
/* export binary object */
if (typecast_builtins[i].values == typecast_BINARY_types) {
psyco_default_binary_cast = (PyObject *)t;
}
}
/* create and save a default cast object (but does not register it) */
psyco_default_cast = typecast_from_c(&typecast_default);
/* register the date/time typecasters with their original names */
#ifdef HAVE_MXDATETIME
for (i = 0; typecast_mxdatetime[i].name != NULL; i++) {
typecastObject *t;
Dprintf("typecast_init: initializing %s", typecast_mxdatetime[i].name);
t = (typecastObject *)typecast_from_c(&(typecast_mxdatetime[i]));
if (t == NULL) return -1;
PyDict_SetItem(dict, t->name, (PyObject *)t);
}
#endif
#ifdef HAVE_PYDATETIME
for (i = 0; typecast_pydatetime[i].name != NULL; i++) {
typecastObject *t;
Dprintf("typecast_init: initializing %s", typecast_pydatetime[i].name);
t = (typecastObject *)typecast_from_c(&(typecast_pydatetime[i]));
if (t == NULL) return -1;
PyDict_SetItem(dict, t->name, (PyObject *)t);
}
#endif
return 0;
}
/* typecast_add - add a type object to the dictionary */
int
typecast_add(PyObject *obj, int binary)
{
PyObject *val;
int len, i;
typecastObject *type = (typecastObject *)obj;
Dprintf("typecast_add: object at %p, values refcnt = %d",
obj, type->values->ob_refcnt);
len = PyTuple_Size(type->values);
for (i = 0; i < len; i++) {
val = PyTuple_GetItem(type->values, i);
Dprintf("typecast_add: adding val: %ld", PyInt_AsLong(val));
if (binary) {
PyDict_SetItem(psyco_binary_types, val, obj);
}
else {
PyDict_SetItem(psyco_types, val, obj);
}
}
return 0;
}
/** typecast type **/
#define OFFSETOF(x) offsetof(typecastObject, x)
static struct memberlist typecastObject_memberlist[] = {
{"name", T_OBJECT, OFFSETOF(name), RO},
{"values", T_OBJECT, OFFSETOF(values), RO},
{NULL}
};
/* numeric methods */
static PyObject *
typecast_new(PyObject *name, PyObject *values, PyObject *cast);
static int
typecast_coerce(PyObject **pv, PyObject **pw)
{
if (PyObject_TypeCheck(*pv, &typecastType)) {
if (PyInt_Check(*pw)) {
PyObject *coer, *args;
args = PyTuple_New(1);
Py_INCREF(*pw);
PyTuple_SET_ITEM(args, 0, *pw);
coer = typecast_new(NULL, args, NULL);
*pw = coer;
Py_DECREF(args);
Py_INCREF(*pv);
return 0;
}
else if (PyObject_TypeCheck(*pw, &typecastType)){
Py_INCREF(*pv);
Py_INCREF(*pw);
return 0;
}
}
PyErr_SetString(PyExc_TypeError, "psycopg type coercion failed");
return -1;
}
static PyNumberMethods typecastObject_as_number = {
0, /*nb_add*/
0, /*nb_subtract*/
0, /*nb_multiply*/
0, /*nb_divide*/
0, /*nb_remainder*/
0, /*nb_divmod*/
0, /*nb_power*/
0, /*nb_negative*/
0, /*nb_positive*/
0, /*nb_absolute*/
0, /*nb_nonzero*/
0, /*nb_invert*/
0, /*nb_lshift*/
0, /*nb_rshift*/
0, /*nb_and*/
0, /*nb_xor*/
0, /*nb_or*/
typecast_coerce, /*nb_coerce*/
0, /*nb_int*/
0, /*nb_long*/
0, /*nb_float*/
0, /*nb_oct*/
0, /*nb_hex*/
};
/* object methods */
static int
typecast_cmp(typecastObject *self, typecastObject *v)
{
int res;
if (PyObject_Length(v->values) > 1 && PyObject_Length(self->values) == 1) {
/* calls itself exchanging the args */
return typecast_cmp(v, self);
}
res = PySequence_Contains(self->values, PyTuple_GET_ITEM(v->values, 0));
if (res < 0) return res;
else return res == 1 ? 0 : 1;
}
static struct PyMethodDef typecastObject_methods[] = {
{"__cmp__", (PyCFunction)typecast_cmp, METH_VARARGS, NULL},
{NULL, NULL}
};
/** FIXME: typecast should become a new-style type sometime in the future, but
right now this is not important and we keep going with old class */
static PyObject *
typecast_getattr(typecastObject *self, char *name)
{
PyObject *rv;
rv = PyMember_Get((char *)self, typecastObject_memberlist, name);
if (rv) return rv;
PyErr_Clear();
return Py_FindMethod(typecastObject_methods, (PyObject *)self, name);
}
static void
typecast_destroy(typecastObject *self)
{
PyObject *name, *cast, *values;
values = self->values;
name = self->name;
cast = self->pcast;
PyObject_Del(self);
Py_XDECREF(name);
Py_XDECREF(values);
Py_XDECREF(cast);
Dprintf("typecast_destroy: object at %p destroyed", self);
}
static PyObject *
typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs)
{
PyObject *string, *cursor, *res;
typecastObject *self = (typecastObject *)obj;
if (!PyArg_ParseTuple(args, "OO", &string, &cursor)) {
return NULL;
}
if (self->ccast) {
Dprintf("typecast_call: calling C cast function");
res = self->ccast(string, cursor);
}
else if (self->pcast) {
Dprintf("typecast_call: calling python callable");
Py_INCREF(string);
res = PyObject_CallFunction(self->pcast, "OO", string, cursor);
}
else {
Py_INCREF(Py_None);
res = Py_None;
}
Dprintf("typecast_call: string argument has refcnt = %d",
string->ob_refcnt);
return res;
}
PyTypeObject typecastType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"psycopg.type", /*tp_name*/
sizeof(typecastObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)typecast_destroy, /*tp_dealloc*/
0, /*tp_print*/
(getattrfunc)typecast_getattr, /*tp_getattr*/
0, /*tp_setattr*/
(cmpfunc)typecast_cmp, /*tp_compare*/
0, /*tp_repr*/
&typecastObject_as_number, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash*/
typecast_call, /*tp_call*/
0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
"psycopg type-casting object" /* Documentation string */
};
static PyObject *
typecast_new(PyObject *name, PyObject *values, PyObject *cast)
{
typecastObject *obj;
obj = PyObject_NEW(typecastObject, &typecastType);
if (obj == NULL) return NULL;
Py_INCREF(values);
obj->values = values;
if (name) {
Py_INCREF(name);
obj->name = name;
}
else {
Py_INCREF(Py_None);
obj->name = Py_None;
}
obj->pcast = NULL;
obj->ccast = NULL;
if (cast && cast != Py_None) {
Py_INCREF(cast);
obj->pcast = cast;
}
Dprintf("typecast_new: typecast object created at %p", obj);
return (PyObject *)obj;
}
PyObject *
typecast_from_python(PyObject *self, PyObject *args, PyObject *keywds)
{
PyObject *v, *name, *cast = NULL;
static char *kwlist[] = {"values", "name", "castobj", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!|O!O", kwlist,
&PyTuple_Type, &v,
&PyString_Type, &name,
&cast)) {
return NULL;
}
return typecast_new(name, v, cast);
}
PyObject *
typecast_from_c(typecastObject_initlist *type)
{
PyObject *tuple;
typecastObject *obj;
int i, len = 0;
while (type->values[len] != 0) len++;
tuple = PyTuple_New(len);
if (!tuple) return NULL;
for (i = 0; i < len ; i++) {
PyTuple_SET_ITEM(tuple, i, PyInt_FromLong(type->values[i]));
}
obj = (typecastObject *)
typecast_new(PyString_FromString(type->name), tuple, NULL);
if (obj) {
obj->ccast = type->cast;
obj->pcast = NULL;
}
return (PyObject *)obj;
}

77
psycopg/typecast.h Normal file
View File

@ -0,0 +1,77 @@
/* typecast.h - definitions for typecasters
*
* Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PSYCOPG_TYPECAST_H
#define PSYCOPG_TYPECAST_H 1
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
/* type of type-casting functions (both C and Python) */
typedef PyObject *(*typecast_function)(PyObject *, PyObject *);
/** typecast type **/
extern PyTypeObject typecastType;
typedef struct {
PyObject_HEAD
PyObject *name; /* the name of this type */
PyObject *values; /* the different types this instance can match */
typecast_function ccast; /* the C casting function */
PyObject *pcast; /* the python casting function */
} typecastObject;
/* the initialization values are stored here */
typedef struct {
char *name;
long int *values;
typecast_function cast;
} typecastObject_initlist;
/* the type dictionary, much faster to access it globally */
extern PyObject *psyco_types;
extern PyObject *psyco_binary_types;
/* the default casting objects, used when no other objects are available */
extern PyObject *psyco_default_cast;
extern PyObject *psyco_default_binary_cast;
/** exported functions **/
/* used by module.c to init the type system and register types */
extern int typecast_init(PyObject *dict);
extern int typecast_add(PyObject *obj, int binary);
/* the C callable typecastObject creator function */
extern PyObject *typecast_from_c(typecastObject_initlist *type);
/* the python callable typecast creator function */
extern PyObject *typecast_from_python(
PyObject *self, PyObject *args, PyObject *keywds);
#endif /* !defined(PSYCOPG_TYPECAST_H) */

192
psycopg/typecast_basic.c Normal file
View File

@ -0,0 +1,192 @@
/* pgcasts_basic.c - basic typecasting functions to python types
*
* Copyright (C) 2001-2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of the psycopg module.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <libpq-fe.h>
/** INTEGER - cast normal integers (4 bytes) to python int **/
static PyObject *
typecast_INTEGER_cast(PyObject *s, PyObject *curs)
{
if (s == Py_None) {Py_INCREF(s); return s;}
return PyNumber_Int(s);
}
/** LONGINTEGER - cast long integers (8 bytes) to python long **/
static PyObject *
typecast_LONGINTEGER_cast(PyObject *s, PyObject *curs)
{
if (s == Py_None) {Py_INCREF(s); return s;}
return PyNumber_Long(s);
}
/** FLOAT - cast floating point numbers to python float **/
static PyObject *
typecast_FLOAT_cast(PyObject *s, PyObject *curs)
{
if (s == Py_None) {Py_INCREF(s); return s;}
return PyNumber_Float(s);
}
/** STRING - cast strings of any type to python string **/
static PyObject *
typecast_STRING_cast(PyObject *s, PyObject *curs)
{
Py_INCREF(s);
return s;
}
/** UNICODE - cast strings of any type to a python unicode object **/
static PyObject *
typecast_UNICODE_cast(PyObject *s, PyObject *curs)
{
PyObject *enc;
if (s == Py_None) {Py_INCREF(s); return s;}
enc = PyDict_GetItemString(psycoEncodings,
((cursorObject*)curs)->conn->encoding);
if (enc) {
return PyUnicode_Decode(PyString_AsString(s),
PyString_Size(s),
PyString_AsString(enc),
NULL);
}
else {
PyErr_Format(InterfaceError,
"can't decode into unicode string from %s",
((cursorObject*)curs)->conn->encoding);
return NULL;
}
}
/** BINARY - cast a binary string into a python string **/
/* the function typecast_BINARY_cast_unescape is used when libpq does not
provide PQunescapeBytea: it convert all the \xxx octal sequences to the
proper byte value */
#ifdef PSYCOPG_OWN_QUOTING
static unsigned char *
typecast_BINARY_cast_unescape(unsigned char *str, size_t *to_length)
{
char *dstptr, *dststr;
int len, i;
len = strlen(str);
dststr = (char*)calloc(len, sizeof(char));
dstptr = dststr;
if (dststr == NULL) return NULL;
Py_BEGIN_ALLOW_THREADS;
for (i = 0; i < len; i++) {
if (str[i] == '\\') {
if ( ++i < len) {
if (str[i] == '\\') {
*dstptr = '\\';
}
else {
*dstptr = 0;
*dstptr |= (str[i++] & 7) << 6;
*dstptr |= (str[i++] & 7) << 3;
*dstptr |= (str[i] & 7);
}
}
}
else {
*dstptr = str[i];
}
dstptr++;
}
Py_END_ALLOW_THREADS;
*to_length = (size_t)(dstptr-dststr);
return dststr;
}
#define PQunescapeBytea typecast_BINARY_cast_unescape
#endif
static PyObject *
typecast_BINARY_cast(PyObject *s, PyObject *curs)
{
PyObject *res;
unsigned char *str;
size_t len;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PQunescapeBytea(PyString_AS_STRING(s), &len);
Dprintf("typecast_BINARY_cast: unescaped %d bytes", len);
/* TODO: using a PyBuffer would make this a zero-copy operation but we'll
need to define our own buffer-derived object to keep a reference to the
memory area: does it buy it?
res = PyBuffer_FromMemory((void*)str, len); */
res = PyString_FromStringAndSize(str, len);
free(str);
return res;
}
/** BOOLEAN - cast boolean value into right python object **/
static PyObject *
typecast_BOOLEAN_cast(PyObject *s, PyObject *curs)
{
PyObject *res;
if (s == Py_None) {Py_INCREF(s); return s;}
if (PyString_AS_STRING(s)[0] == 't')
res = Py_True;
else
res = Py_False;
Py_INCREF(res);
return res;
}
/** DECIMAL - cast any kind of number into a Python Decimal object **/
#ifdef HAVE_DECIMAL
static PyObject *
typecast_DECIMAL_cast(PyObject *s, PyObject *curs)
{
if (s == Py_None) {Py_INCREF(s); return s;}
return PyObject_CallFunction(decimalType, "O", s);
}
#else
#define typecast_DECIMAL_cast typecast_FLOAT_cast
#endif
/* some needed aliases */
#define typecast_NUMBER_cast typecast_FLOAT_cast
#define typecast_ROWID_cast typecast_INTEGER_cast

View File

@ -0,0 +1,147 @@
/* pgcasts_basic.c - basic typecasting functions to python types
*
* Copyright (C) 2001-2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of the psycopg module.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <libpq-fe.h>
/** INTEGER - cast normal integers (4 bytes) to python int **/
static PyObject *
typecast_INTEGER_cast(PyObject *s, PyObject *curs)
{
if (s == Py_None) {Py_INCREF(s); return s;}
return PyNumber_Int(s);
}
/** LONGINTEGER - cast long integers (8 bytes) to python long **/
static PyObject *
typecast_LONGINTEGER_cast(PyObject *s, PyObject *curs)
{
if (s == Py_None) {Py_INCREF(s); return s;}
return PyNumber_Long(s);
}
/** FLOAT - cast floating point numbers to python float **/
static PyObject *
typecast_FLOAT_cast(PyObject *s, PyObject *curs)
{
if (s == Py_None) {Py_INCREF(s); return s;}
return PyNumber_Float(s);
}
/** STRING - cast strings of any type to python string **/
static PyObject *
typecast_STRING_cast(PyObject *s, PyObject *curs)
{
Py_INCREF(s);
return s;
}
/** BINARY - cast a binary string into a python string **/
/* the function typecast_BINARY_cast_unescape is used when libpq does not
provide PQunescapeBytea: it convert all the \xxx octal sequences to the
proper byte value */
#ifdef PSYCOPG_OWN_QUOTING
static unsigned char *
typecast_BINARY_cast_unescape(unsigned char *str, size_t *to_length)
{
char *dstptr, *dststr;
int len, i;
len = strlen(str);
dststr = (char*)calloc(len, sizeof(char));
dstptr = dststr;
if (dststr == NULL) return NULL;
Py_BEGIN_ALLOW_THREADS;
for (i = 0; i < len; i++) {
if (str[i] == '\\') {
if ( ++i < len) {
if (str[i] == '\\') {
*dstptr = '\\';
}
else {
*dstptr = 0;
*dstptr |= (str[i++] & 7) << 6;
*dstptr |= (str[i++] & 7) << 3;
*dstptr |= (str[i] & 7);
}
}
}
else {
*dstptr = str[i];
}
dstptr++;
}
Py_END_ALLOW_THREADS;
*to_length = (size_t)(dstptr-dststr);
return dststr;
}
#define PQunescapeBytea typecast_BINARY_cast_unescape
#endif
static PyObject *
typecast_BINARY_cast(PyObject *s, PyObject *curs)
{
PyObject *res;
unsigned char *str;
size_t len;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PQunescapeBytea(PyString_AS_STRING(s), &len);
res = PyBuffer_FromMemory((void*)str, len);
free(str);
return res;
}
/** BOOLEAN - cast boolean value into right python object **/
static PyObject *
typecast_BOOLEAN_cast(PyObject *s, PyObject *curs)
{
PyObject *res;
if (s == Py_None) {Py_INCREF(s); return s;}
if (PyString_AS_STRING(s)[0] == 't')
res = Py_True;
else
res = Py_False;
Py_INCREF(res);
return res;
}
/* some needed aliases */
#define typecast_NUMBER_cast typecast_FLOAT_cast
#define typecast_ROWID_cast typecast_INTEGER_cast

View File

@ -0,0 +1,34 @@
static long int typecast_NUMBER_types[] = {20, 23, 21, 701, 700, 1700, 0};
static long int typecast_LONGINTEGER_types[] = {20, 0};
static long int typecast_INTEGER_types[] = {23, 21, 0};
static long int typecast_FLOAT_types[] = {701, 700, 0};
static long int typecast_DECIMAL_types[] = {1700, 0};
static long int typecast_UNICODE_types[] = {19, 18, 25, 1042, 1043, 0};
static long int typecast_STRING_types[] = {19, 18, 25, 1042, 1043, 0};
static long int typecast_BOOLEAN_types[] = {16, 0};
static long int typecast_DATETIME_types[] = {1114, 1184, 704, 1186, 0};
static long int typecast_TIME_types[] = {1083, 1266, 0};
static long int typecast_DATE_types[] = {1082, 0};
static long int typecast_INTERVAL_types[] = {704, 1186, 0};
static long int typecast_BINARY_types[] = {17, 0};
static long int typecast_ROWID_types[] = {26, 0};
typecastObject_initlist typecast_builtins[] = {
{"NUMBER", typecast_NUMBER_types, typecast_NUMBER_cast},
{"LONGINTEGER", typecast_LONGINTEGER_types, typecast_LONGINTEGER_cast},
{"INTEGER", typecast_INTEGER_types, typecast_INTEGER_cast},
{"FLOAT", typecast_FLOAT_types, typecast_FLOAT_cast},
{"DECIMAL", typecast_DECIMAL_types, typecast_DECIMAL_cast},
{"UNICODE", typecast_UNICODE_types, typecast_UNICODE_cast},
{"STRING", typecast_STRING_types, typecast_STRING_cast},
{"BOOLEAN", typecast_BOOLEAN_types, typecast_BOOLEAN_cast},
{"DATETIME", typecast_DATETIME_types, typecast_DATETIME_cast},
{"TIME", typecast_TIME_types, typecast_TIME_cast},
{"DATE", typecast_DATE_types, typecast_DATE_cast},
{"INTERVAL", typecast_INTERVAL_types, typecast_INTERVAL_cast},
{"BINARY", typecast_BINARY_types, typecast_BINARY_cast},
{"ROWID", typecast_ROWID_types, typecast_ROWID_cast},
{NULL, NULL, NULL}
};

287
psycopg/typecast_datetime.c Normal file
View File

@ -0,0 +1,287 @@
/* typecast_datetime.c - date and time typecasting functions to python types
*
* Copyright (C) 2001-2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of the psycopg module.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <math.h>
#include "datetime.h"
/* the pointer to the datetime module API is initialized by the module init
code, we just need to grab it */
extern PyObject* pyDateTimeModuleP;
extern PyObject *pyDateTypeP;
extern PyObject *pyTimeTypeP;
extern PyObject *pyDateTimeTypeP;
extern PyObject *pyDeltaTypeP;
/** DATE - cast a date into a date python object **/
static PyObject *
typecast_PYDATE_cast(PyObject *s, PyObject *curs)
{
PyObject* obj = NULL;
int n, y=0, m=0, d=0;
char *str;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PyString_AsString(s);
/* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') {
obj = PyObject_GetAttrString(pyDateTypeP, "min");
}
else {
obj = PyObject_GetAttrString(pyDateTypeP, "max");
}
}
else {
n = sscanf(str, "%d-%d-%d", &y, &m, &d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
}
else {
obj = PyObject_CallFunction(pyDateTypeP, "iii", y, m, d);
}
}
return obj;
}
/** DATETIME - cast a timestamp into a datetime python object **/
static PyObject *
typecast_PYDATETIME_cast(PyObject *s, PyObject *curs)
{
PyObject* obj = NULL;
int n, y=0, m=0, d=0;
int hh=0, mm=0;
int tzh=0, tzm=0;
double ss=0.0;
char tzs=0, *str;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PyString_AsString(s);
/* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') {
obj = PyObject_GetAttrString(pyDateTimeModuleP, "min");
}
else {
obj = PyObject_GetAttrString(pyDateTimeModuleP, "max");
}
}
else {
Dprintf("typecast_PYDATETIME_cast: s = %s", str);
n = sscanf(str, "%d-%d-%d %d:%d:%lf%c%d:%d",
&y, &m, &d, &hh, &mm, &ss, &tzs, &tzh, &tzm);
Dprintf("typecast_PYDATETIME_cast: date parsed, %d components", n);
if (n != 3 && n != 6 && n <= 7) {
PyErr_SetString(DataError, "unable to parse date");
}
else {
double micro = (ss - floor(ss)) * 1000000.0;
int sec = (int)floor(ss);
if (sec > 59) {
mm += 1;
sec -= 60;
}
if (tzs && ((cursorObject*)curs)->tzinfo_factory != Py_None) {
/* we have a time zone, calculate minutes and create
appropriate tzinfo object calling the factory */
PyObject *tzinfo;
tzm += tzh*60;
if (tzs == '-') tzm = -tzm;
Dprintf("typecast_PYDATETIME_cast: UTC offset = %dm", tzm);
tzinfo = PyObject_CallFunction(
((cursorObject*)curs)->tzinfo_factory, "i", tzm);
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiiiO",
y, m, d, hh, mm, sec, (int)round(micro), tzinfo);
Dprintf("typecast_PYDATETIME_cast: tzinfo: %p, refcnt = %d",
tzinfo, tzinfo->ob_refcnt);
Py_XDECREF(tzinfo);
}
else {
obj = PyObject_CallFunction(pyDateTimeTypeP, "iiiiiii",
y, m, d, hh, mm, sec, (int)round(micro));
}
}
}
return obj;
}
/** TIME - parse time into a time object **/
static PyObject *
typecast_PYTIME_cast(PyObject *s, PyObject *curs)
{
PyObject* obj = NULL;
int n, hh=0, mm=0;
double ss=0.0;
char *str;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PyString_AsString(s);
n = sscanf(str, "%d:%d:%lf", &hh, &mm, &ss);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse time");
}
else {
double micro = (ss - floor(ss)) * 1000000.0;
int sec = (int)floor(ss);
if (sec > 59) {
mm += 1;
sec -= 60;
}
obj = PyObject_CallFunction(pyTimeTypeP, "iiii",
hh, mm, sec, (int)round(micro));
}
return obj;
}
/** INTERVAL - parse an interval into a timedelta object **/
static PyObject *
typecast_PYINTERVAL_cast(PyObject *s, PyObject *curs)
{
long years = 0, months = 0, days = 0, denominator = 1;
double hours = 0.0, minutes = 0.0, seconds = 0.0, hundredths = 0.0;
double v = 0.0, sign = 1.0;
int part = 0, sec;
double micro;
char *str;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PyString_AsString(s);
Dprintf("typecast_PYINTERVAL_cast: s = %s", str);
while (*str) {
switch (*str) {
case '-':
sign = -1.0;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
v = v*10 + (double)*str - (double)'0';
if (part == 6){
denominator *= 10;
}
break;
case 'y':
if (part == 0) {
years = (long)(v*sign);
str = skip_until_space(str);
v = 0.0; sign = 1.0; part = 1;
}
break;
case 'm':
if (part <= 1) {
months = (long)(v*sign);
str = skip_until_space(str);
v = 0.0; sign = 1.0; part = 2;
}
break;
case 'd':
if (part <= 2) {
days = (long)(v*sign);
str = skip_until_space(str);
v = 0.0; sign = 1.0; part = 3;
}
break;
case ':':
if (part <= 3) {
hours = v;
v = 0.0; part = 4;
}
else if (part == 4) {
minutes = v;
v = 0.0; part = 5;
}
break;
case '.':
if (part == 5) {
seconds = v;
v = 0.0; part = 6;
}
break;
default:
break;
}
str++;
}
/* manage last value, be it minutes or seconds or hundredths of a second */
if (part == 4) {
minutes = v;
}
else if (part == 5) {
seconds = v;
}
else if (part == 6) {
hundredths = v;
hundredths = hundredths/denominator;
}
/* calculates seconds */
if (sign < 0.0) {
seconds = - (hundredths + seconds + minutes*60 + hours*3600);
}
else {
seconds += hundredths + minutes*60 + hours*3600;
}
/* calculates days */
days += years*365 + months*30;
micro = (seconds - floor(seconds)) * 1000000.0;
sec = (int)floor(seconds);
return PyObject_CallFunction(pyDeltaTypeP, "iii",
days, sec, (int)round(micro));
}
/* psycopg defaults to using python datetime types */
#ifdef PSYCOPG_DEFAULT_PYDATETIME
#define typecast_DATE_cast typecast_PYDATE_cast
#define typecast_TIME_cast typecast_PYTIME_cast
#define typecast_INTERVAL_cast typecast_PYINTERVAL_cast
#define typecast_DATETIME_cast typecast_PYDATETIME_cast
#endif

View File

@ -0,0 +1,222 @@
/* typecast_mxdatetime.c - date and time typecasting functions to mx types
*
* Copyright (C) 2001-2003 Federico Di Gregorio <fog@debian.org>
*
* This file is part of the psycopg module.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "mxDateTime.h"
/* the pointer to the mxDateTime API is initialized by the module init code,
we just need to grab it */
extern mxDateTimeModule_APIObject *mxDateTimeP;
/** DATE - cast a date into mx.DateTime python object **/
static PyObject *
typecast_MXDATE_cast(PyObject *s, PyObject *curs)
{
int n, y=0, m=0, d=0;
int hh=0, mm=0;
double ss=0.0;
char *str;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PyString_AsString(s);
/* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') {
return mxDateTimeP->DateTime_FromDateAndTime(-999998,1,1, 0,0,0);
}
else {
return mxDateTimeP->DateTime_FromDateAndTime(999999,12,31, 0,0,0);
}
}
Dprintf("typecast_MXDATE_cast: s = %s", str);
n = sscanf(str, "%d-%d-%d %d:%d:%lf", &y, &m, &d, &hh, &mm, &ss);
Dprintf("typecast_MXDATE_cast: date parsed, %d components", n);
if (n != 3 && n != 6) {
PyErr_SetString(DataError, "unable to parse date");
return NULL;
}
return mxDateTimeP->DateTime_FromDateAndTime(y, m, d, hh, mm, ss);
}
/** TIME - parse time into an mx.DateTime object **/
static PyObject *
typecast_MXTIME_cast(PyObject *s, PyObject *curs)
{
int n, hh=0, mm=0;
double ss=0.0;
char *str;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PyString_AsString(s);
Dprintf("typecast_MXTIME_cast: s = %s", str);
n = sscanf(str, "%d:%d:%lf", &hh, &mm, &ss);
Dprintf("typecast_MXTIME_cast: time parsed, %d components", n);
Dprintf("typecast_MXTIME_cast: hh = %d, mm = %d, ss = %f", hh, mm, ss);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse time");
return NULL;
}
return mxDateTimeP->DateTimeDelta_FromTime(hh, mm ,ss);
}
/** INTERVAL - parse an interval into an mx.DateTimeDelta **/
static PyObject *
typecast_MXINTERVAL_cast(PyObject *s, PyObject *curs)
{
long years = 0, months = 0, days = 0, denominator = 1;
double hours = 0.0, minutes = 0.0, seconds = 0.0, hundredths = 0.0;
double v = 0.0, sign = 1.0;
int part = 0;
char *str;
if (s == Py_None) {Py_INCREF(s); return s;}
str = PyString_AsString(s);
Dprintf("typecast_MXINTERVAL_cast: s = %s", str);
while (*str) {
switch (*str) {
case '-':
sign = -1.0;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
v = v*10 + (double)*str - (double)'0';
Dprintf("typecast_MXINTERVAL_cast: v = %f", v);
if (part == 6){
denominator *= 10;
Dprintf("typecast_MXINTERVAL_cast: denominator = %ld",
denominator);
}
break;
case 'y':
if (part == 0) {
years = (long)(v*sign);
str = skip_until_space(str);
Dprintf("typecast_MXINTERVAL_cast: years = %ld, rest = %s",
years, str);
v = 0.0; sign = 1.0; part = 1;
}
break;
case 'm':
if (part <= 1) {
months = (long)(v*sign);
str = skip_until_space(str);
Dprintf("typecast_MXINTERVAL_cast: months = %ld, rest = %s",
months, str);
v = 0.0; sign = 1.0; part = 2;
}
break;
case 'd':
if (part <= 2) {
days = (long)(v*sign);
str = skip_until_space(str);
Dprintf("typecast_MXINTERVAL_cast: days = %ld, rest = %s",
days, str);
v = 0.0; sign = 1.0; part = 3;
}
break;
case ':':
if (part <= 3) {
hours = v;
Dprintf("typecast_MXINTERVAL_cast: hours = %f", hours);
v = 0.0; part = 4;
}
else if (part == 4) {
minutes = v;
Dprintf("typecast_MXINTERVAL_cast: minutes = %f", minutes);
v = 0.0; part = 5;
}
break;
case '.':
if (part == 5) {
seconds = v;
Dprintf("typecast_MXINTERVAL_cast: seconds = %f", seconds);
v = 0.0; part = 6;
}
break;
default:
break;
}
str++;
}
/* manage last value, be it minutes or seconds or hundredths of a second */
if (part == 4) {
minutes = v;
Dprintf("typecast_MXINTERVAL_cast: minutes = %f", minutes);
}
else if (part == 5) {
seconds = v;
Dprintf("typecast_MXINTERVAL_cast: seconds = %f", seconds);
}
else if (part == 6) {
hundredths = v;
Dprintf("typecast_MXINTERVAL_cast: hundredths = %f", hundredths);
hundredths = hundredths/denominator;
Dprintf("typecast_MXINTERVAL_cast: fractions = %.20f", hundredths);
}
/* calculates seconds */
if (sign < 0.0) {
seconds = - (hundredths + seconds + minutes*60 + hours*3600);
}
else {
seconds += hundredths + minutes*60 + hours*3600;
}
/* calculates days */
days += years*365 + months*30;
Dprintf("typecast_MXINTERVAL_cast: days = %ld, seconds = %f",
days, seconds);
return mxDateTimeP->DateTimeDelta_FromDaysAndSeconds(days, seconds);
}
/* psycopg defaults to using mx types */
#ifdef PSYCOPG_DEFAULT_MXDATETIME
#define typecast_DATE_cast typecast_MXDATE_cast
#define typecast_TIME_cast typecast_MXTIME_cast
#define typecast_INTERVAL_cast typecast_MXINTERVAL_cast
#define typecast_DATETIME_cast typecast_MXDATE_cast
#endif

13
sandbox/pbool.py Normal file
View File

@ -0,0 +1,13 @@
class B(object):
def __init__(self, x):
if x: self._o = True
else: self._o = False
def __getattribute__(self, attr):
print "ga called", attr
return object.__getattribute__(self, attr)
def _sqlquote(self):
if self._o == True:
return 'It is True'
else:
return 'It is False'

19
sandbox/stress.py Normal file
View File

@ -0,0 +1,19 @@
import psycopg
import psycopg.extras
conn = psycopg.connect('dbname=test')
#curs = conn.cursor()
#curs.execute("CREATE TABLE itest (n int4)")
#for i in xrange(10000000):
# curs = conn.cursor()
# curs.execute("INSERT INTO itest VALUES (1)")
# curs.execute("SELECT '2003-12-12 10:00:00'::timestamp AS foo")
# curs.execute("SELECT 'xxx' AS foo")
# curs.fetchall()
# curs.close()
curs = conn.cursor(factory=psycopg.extras.DictCursor)
curs.execute("select 1 as foo")
x = curs.fetchone()
print x['foo']

12
sandbox/test.py Normal file
View File

@ -0,0 +1,12 @@
import datetime
import psycopg
#d = datetime.timedelta(12, 100, 9876)
#print d.days, d.seconds, d.microseconds
#print psycopg.adapt(d).getquoted()
o = psycopg.connect("dbname=test")
c = o.cursor()
c.execute("SELECT 1.0 AS foo")
print c.fetchmany(2)
print c.fetchall()

100
scripts/buildtypes.py Normal file
View File

@ -0,0 +1,100 @@
# -*- python -*-
#
# Copyright (C) 2001-2003 Federico Di Gregorio <fog@debian.org>
#
# This file is part of the psycopg module.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2,
# or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# this a little script that analyze a file with (TYPE, NUMBER) tuples
# and write out C code ready for inclusion in psycopg. the generated
# code defines the DBAPITypeObject fundamental types and warns for
# undefined types.
import sys, os, string, copy
from string import split, join, strip
# here is the list of the foundamental types we want to import from
# postgresql header files
basic_types = (['NUMBER', ['INT8', 'INT4', 'INT2', 'FLOAT8', 'FLOAT4',
'NUMERIC']],
['LONGINTEGER', ['INT8']],
['INTEGER', ['INT4', 'INT2']],
['FLOAT', ['FLOAT8', 'FLOAT4']],
['DECIMAL', ['NUMERIC']],
['UNICODE', ['NAME', 'CHAR', 'TEXT', 'BPCHAR',
'VARCHAR']],
['STRING', ['NAME', 'CHAR', 'TEXT', 'BPCHAR',
'VARCHAR']],
['BOOLEAN', ['BOOL']],
['DATETIME', ['TIMESTAMP', 'TIMESTAMPTZ',
'TINTERVAL', 'INTERVAL']],
['TIME', ['TIME', 'TIMETZ']],
['DATE', ['DATE']],
['INTERVAL', ['TINTERVAL', 'INTERVAL']],
['BINARY', ['BYTEA']],
['ROWID', ['OID']])
# this is the header used to compile the data in the C module
HEADER = """
typecastObject_initlist typecast_builtins[] = {
"""
# then comes the footer
FOOTER = """ {NULL, NULL, NULL}\n};\n"""
# usefull error reporting function
def error(msg):
"""Report an error on stderr."""
sys.stderr.write(msg+'\n')
# read couples from stdin and build list
read_types = []
for l in sys.stdin.readlines():
oid, val = split(l)
read_types.append((strip(oid)[:-3], strip(val)))
# look for the wanted types in the read touples
found_types = {}
for t in basic_types:
k = t[0]
found_types[k] = []
for v in t[1]:
found = filter(lambda x, y=v: x[0] == y, read_types)
if len(found) == 0:
error(v+': value not found')
elif len(found) > 1:
error(v+': too many values')
else:
found_types[k].append(int(found[0][1]))
# now outputs to stdout the right C-style definitions
stypes = "" ; sstruct = ""
for t in basic_types:
k = t[0]
s = str(found_types[k])
s = '{' + s[1:-1] + ', 0}'
stypes = stypes + ('static long int typecast_%s_types[] = %s;\n' % (k, s))
sstruct = sstruct + (' {"%s", typecast_%s_types, typecast_%s_cast},\n'
% (k, k, k))
sstruct = HEADER + sstruct + FOOTER
print stypes
print sstruct

44
scripts/maketypes.sh Normal file
View File

@ -0,0 +1,44 @@
#!/bin/sh
SCRIPTSDIR="`dirname $0`"
SRCDIR="`dirname $SCRIPTSDIR`/psycopg"
if [ -z "$1" ] ; then
echo Usage: $0 '<postgresql include directory>'
exit 1
fi
echo -n checking for pg_type.h ...
if [ -f "$1/catalog/pg_type.h" ] ; then
PGTYPE="$1/catalog/pg_type.h"
else
if [ -f "$1/server/catalog/pg_type.h" ] ; then
PGTYPE="$1/server/catalog/pg_type.h"
else
echo
echo "error: can't find pg_type.h under $1"
exit 2
fi
fi
echo " found"
PGVERSION="`sed -n -e 's/.*PG_VERSION \"\([0-9]\.[0-9]\).*\"/\1/p' $1/pg_config.h`"
PGMAJOR="`echo $PGVERSION | cut -d. -f1`"
PGMINOR="`echo $PGVERSION | cut -d. -f2`"
echo checking for postgresql major: $PGMAJOR
echo checking for postgresql minor: $PGMINOR
echo -n generating pgtypes.h ...
awk '/#define .+OID/ {print "#define " $2 " " $3}' "$PGTYPE" \
> $SRCDIR/pgtypes.h
echo " done"
echo -n generating typecast_builtins.c ...
awk '/#define .+OID/ {print $2 " " $3}' "$PGTYPE" | \
python $SCRIPTSDIR/buildtypes.py >$SRCDIR/typecast_builtins.c
echo " done"
echo -n generating pgversion.h ...
echo "#define PG_VERSION_MAJOR $PGMAJOR" >$SRCDIR/pgversion.h
echo "#define PG_VERSION_MINOR $PGMINOR" >>$SRCDIR/pgversion.h
echo " done"

12
setup.cfg Normal file
View File

@ -0,0 +1,12 @@
[build_ext]
define=PSYCOPG_DEBUG,PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,HAVE_ASPRINTF,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
# PSYCOPG_OWN_QUOTING can be added above but it is deprecated
# include_dirs is the preferred method for locating postgresql headers,
# but some extra checks on sys.platform will still be done in setup.py
include_dirs=.:/usr/include/postgresql:/usr/include/postgresql/server
# if postgresql is installed somewhere weird, just addthe right path in
# library_dir any extra libraries required to link in libraries
#library_dirs=
libraries=pq

167
setup.py Normal file
View File

@ -0,0 +1,167 @@
# setup.py - distutils packaging
#
# Copyright (C) 2003-2004 Federico Di Gregorio <fog@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# 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
UPDATEs. psycopg 2 also provide full asycronous operations for the really
brave programmer.
"""
classifiers = """\
Development Status :: 4 - Beta
Intended Audience :: Developers
License :: OSI Approved :: GNU General Public License (GPL)
License :: OSI Approved :: Zope Public License
Programming Language :: Python
Programming Language :: C
Programming Language :: SQL
Topic :: Database
Topic :: Database :: Front-Ends
Topic :: Software Development
Topic :: Software Development :: Libraries :: Python Modules
Operating System :: Microsoft :: Windows
Operating System :: Unix
"""
import sys, os.path
from distutils.core import setup, Extension
from distutils.sysconfig import get_python_inc
import distutils.ccompiler
PSYCOPG_VERSION = '1.99.10'
have_pydatetime = False
have_mxdatetime = False
use_pydatetime = True
# windows-only definitions (TODO: this should be moved to setup.cfg!)
POSTGRESQLDIR = "D:\\POSTGRESQL-7.4.2"
USE_PG_DLL = True
# to work around older distutil limitations
if sys.version < '2.2.3':
from distutils.dist import DistributionMetadata
DistributionMetadata.classifiers = None
DistributionMetadata.download_url = None
# let's start with macro definitions (the ones not already in setup.cfg)
define_macros = []
# python version
define_macros.append(('PY_MAJOR_VERSION', str(sys.version_info[0])))
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'))
if sys.version_info[0] >= 2 and sys.version_info[1] >= 4:
define_macros.append(('HAVE_DECIMAL','1'))
# gather information to build the extension module
ext = [] ; data_files = []
library_dirs = [] ; libraries = [] ; include_dirs = []
if sys.platform != 'win32':
define_macros.append(('PSYCOPG_VERSION', '"'+PSYCOPG_VERSION+'"'))
else:
define_macros.append(('PSYCOPG_VERSION', '\\"'+PSYCOPG_VERSION+'\\"'))
include_dirs = ['.',
POSTGRESQLDIR + "\\src\\interfaces\\libpq",
POSTGRESQLDIR + "\\src\\include" ]
library_dirs = [ POSTGRESQLDIR + "\\src\\interfaces\\libpq\\Release" ]
libraries = ["ws2_32"]
if USE_PG_DLL:
data_files.append((".\\lib\site-packages\\",
[POSTGRESQLDIR + "\\src\interfaces\\libpq\\Release\\libpq.dll"]))
libraries.append("libpqdll")
else:
libraries.append("libpq")
libraries.append("advapi32")
# extra checks on darwin
if sys.platform == "darwin":
# fink installs lots of goodies in /sw/... - make sure we check there
include_dirs.append("/sw/include/postgresql")
library_dirs.append("/sw/lib")
# sources
sources = [
'psycopgmodule.c', 'pqpath.c', 'typecast.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']
# check for mx package
mxincludedir = os.path.join(get_python_inc(plat_specific=1), "mx")
if os.path.exists(mxincludedir):
include_dirs.append(mxincludedir)
define_macros.append(('HAVE_MXDATETIME','1'))
sources.append('adapter_mxdatetime.c')
have_mxdatetime = True
# check for python datetime package
if os.path.exists(os.path.join(get_python_inc(plat_specific=1),"datetime.h")):
define_macros.append(('HAVE_PYDATETIME','1'))
sources.append('adapter_datetime.c')
have_pydatetime = True
# 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:
define_macros.append(('PSYCOPG_DEFAULT_PYDATETIME','1'))
elif have_mxdatetime:
define_macros.append(('PSYCOPG_DEFAULT_MXDATETIME','1'))
else:
sys.stderr.write("error: psycopg requires a datetime module:\n")
sys.stderr.write("error: mx.DateTime module not found\n")
sys.stderr.write("error: python datetime module not found\n")
sys.exit(1)
# build the extension
sources = map(lambda x: os.path.join('psycopg', x), sources)
ext.append(Extension("psycopg._psycopg", sources,
include_dirs=include_dirs,
library_dirs=library_dirs,
define_macros=define_macros,
undef_macros=[],
libraries=libraries))
setup(name="psycopg",
version=PSYCOPG_VERSION,
maintainer="Federico Di Gregorio",
maintainer_email="fog@initd.org",
author="Federico Di Gregorio",
author_email="fog@initd.org",
url="http://initd.org/software/initd/psycopg",
download_url = "http://initd.org/software/initd/psycopg",
license="GPL or ZPL",
platforms = ["any"],
description=__doc__.split("\n")[0],
long_description="\n".join(__doc__.split("\n")[2:]),
classifiers=filter(None, classifiers.split("\n")),
data_files=data_files,
package_dir={'psycopg':'lib'},
packages=['psycopg'],
ext_modules=ext)