Initial psycopg 2 import after SVN crash.
8
AUTHORS
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
After Width: | Height: | Size: 924 B |
BIN
ZPsycopgDA/icons/date.gif
Normal file
After Width: | Height: | Size: 930 B |
BIN
ZPsycopgDA/icons/datetime.gif
Normal file
After Width: | Height: | Size: 925 B |
BIN
ZPsycopgDA/icons/field.gif
Normal file
After Width: | Height: | Size: 915 B |
BIN
ZPsycopgDA/icons/float.gif
Normal file
After Width: | Height: | Size: 929 B |
BIN
ZPsycopgDA/icons/int.gif
Normal file
After Width: | Height: | Size: 918 B |
BIN
ZPsycopgDA/icons/stable.gif
Normal file
After Width: | Height: | Size: 884 B |
BIN
ZPsycopgDA/icons/table.gif
Normal file
After Width: | Height: | Size: 878 B |
BIN
ZPsycopgDA/icons/text.gif
Normal file
After Width: | Height: | Size: 918 B |
BIN
ZPsycopgDA/icons/time.gif
Normal file
After Width: | Height: | Size: 926 B |
BIN
ZPsycopgDA/icons/view.gif
Normal file
After Width: | Height: | Size: 893 B |
BIN
ZPsycopgDA/icons/what.gif
Normal file
After Width: | Height: | Size: 894 B |
51
ZPsycopgDA/pool.py
Normal 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
43
doc/HACKING
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
After Width: | Height: | Size: 22 KiB |
160
examples/threads.py
Normal 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
|
@ -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
After Width: | Height: | Size: 34 KiB |
28
lib/__init__.py
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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);
|
||||||
|
}
|
95
psycopg/adapter_datetime.h
Normal 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) */
|
392
psycopg/adapter_mxdatetime.c
Normal 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);
|
||||||
|
}
|
100
psycopg/adapter_mxdatetime.h
Normal 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
|
@ -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);
|
||||||
|
}
|
51
psycopg/adapter_pboolean.h
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
121
psycopg/microprotocols.c
Normal 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
|
@ -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) */
|
211
psycopg/microprotocols_proto.c
Normal 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*/
|
||||||
|
};
|
45
psycopg/microprotocols_proto.h
Normal 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
|
@ -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
|
@ -0,0 +1,2 @@
|
||||||
|
#define PG_VERSION_MAJOR 7
|
||||||
|
#define PG_VERSION_MINOR 4
|
777
psycopg/pqpath.c
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
147
psycopg/typecast_basic.c.old
Normal 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
|
34
psycopg/typecast_builtins.c
Normal 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
|
@ -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
|
222
psycopg/typecast_mxdatetime.c
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|