Merge branch 'devel'

This commit is contained in:
Daniele Varrazzo 2013-04-07 17:43:35 +01:00
commit 80e105c74d
132 changed files with 5217 additions and 7850 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ doc/src/_build/*
doc/html/* doc/html/*
doc/psycopg2.txt doc/psycopg2.txt
env env
.tox

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
language: python
python:
- 2.6
- 2.7
before_script:
- psql -c 'create database psycopg2_test;' -U postgres
install:
- python setup.py install
script: make check

2455
ChangeLog

File diff suppressed because it is too large Load Diff

936
NEWS

File diff suppressed because it is too large Load Diff

View File

@ -1,360 +0,0 @@
# ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection
#
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 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 Lesser General Public
# License for more details.
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
ALLOWED_PSYCOPG_VERSIONS = ('2.4', '2.4.1', '2.4.4', '2.4.5', '2.4.6')
import sys
import time
import db
import re
import Acquisition
import Shared.DC.ZRDB.Connection
from db import DB
from Globals import HTMLFile
from ExtensionClass import Base
from App.Dialogs import MessageDialog
from DateTime import DateTime
# ImageFile is deprecated in Zope >= 2.9
try:
from App.ImageFile import ImageFile
except ImportError:
# Zope < 2.9. If PIL's installed with a .pth file, we're probably
# hosed.
from ImageFile import ImageFile
# import psycopg and functions/singletons needed for date/time conversions
import psycopg2
from psycopg2 import NUMBER, STRING, ROWID, DATETIME
from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE
from psycopg2.extensions import TIME, INTERVAL
from psycopg2.extensions import new_type, register_type
# add a new connection to a folder
manage_addZPsycopgConnectionForm = HTMLFile('dtml/add',globals())
def manage_addZPsycopgConnection(self, id, title, connection_string,
zdatetime=None, tilevel=2,
encoding='', check=None, REQUEST=None):
"""Add a DB connection to a folder."""
self._setObject(id, Connection(id, title, connection_string,
zdatetime, check, tilevel, encoding))
if REQUEST is not None: return self.manage_main(self, REQUEST)
# the connection object
class Connection(Shared.DC.ZRDB.Connection.Connection):
"""ZPsycopg Connection."""
_isAnSQLConnection = 1
id = 'Psycopg2_database_connection'
database_type = 'Psycopg2'
meta_type = title = 'Z Psycopg 2 Database Connection'
icon = 'misc_/conn'
def __init__(self, id, title, connection_string,
zdatetime, check=None, tilevel=2, encoding='UTF-8'):
self.zdatetime = zdatetime
self.id = str(id)
self.edit(title, connection_string, zdatetime,
check=check, tilevel=tilevel, encoding=encoding)
def factory(self):
return DB
## connection parameters editing ##
def edit(self, title, connection_string,
zdatetime, check=None, tilevel=2, encoding='UTF-8'):
self.title = title
self.connection_string = connection_string
self.zdatetime = zdatetime
self.tilevel = tilevel
self.encoding = encoding
if check: self.connect(self.connection_string)
manage_properties = HTMLFile('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 psycopg2.__version__.split(' ')[0] not in ALLOWED_PSYCOPG_VERSIONS:
raise ImportError("psycopg version mismatch (imported %s)" %
psycopg2.__version__)
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.get_type_casts(), self.encoding)
self._v_database_connection.open()
self._v_connected = DateTime()
return self
def get_type_casts(self):
# note that in both cases order *is* important
if self.zdatetime:
return ZDATETIME, ZDATE, ZTIME
else:
return DATETIME, DATE, TIME
## browsing and table/column management ##
manage_options = Shared.DC.ZRDB.Connection.Connection.manage_options
# + (
# {'label': 'Browse', 'action':'manage_browse'},)
#manage_tables = HTMLFile('dtml/tables', globals())
#manage_browse = HTMLFile('dtml/browse', globals())
info = None
def table_info(self):
return self._v_database_connection.table_info()
def __getitem__(self, name):
if name == 'tableNamed':
if not hasattr(self, '_v_tables'): self.tpValues()
return self._v_tables.__of__(self)
raise KeyError, name
def tpValues(self):
res = []
conn = self._v_database_connection
for d in conn.tables(rdb=0):
try:
name = d['TABLE_NAME']
b = TableBrowser()
b.__name__ = name
b._d = d
b._c = c
try:
b.icon = table_icons[d['TABLE_TYPE']]
except:
pass
r.append(b)
except:
pass
return res
## database connection registration data ##
classes = (Connection,)
meta_types = ({'name':'Z Psycopg 2 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('icons/DBAdapterFolder_icon.gif', globals())}
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(iso, curs):
if iso:
if iso in ['-infinity', 'infinity']:
return iso
else:
return DateTime(iso)
# convert an ISO date string from postgres to a Zope DateTime object
def _cast_Date(iso, curs):
if iso:
if iso in ['-infinity', 'infinity']:
return iso
else:
return DateTime(iso)
# 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(iso, curs):
if iso:
if iso in ['-infinity', 'infinity']:
return iso
else:
return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
time.localtime(time.time())[:3]+
time.strptime(iso[:8], "%H:%M:%S")[3:]))
# NOTE: we don't cast intervals anymore because they are passed
# untouched to Zope.
def _cast_Interval(iso, curs):
return iso
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)
## table browsing helpers ##
class TableBrowserCollection(Acquisition.Implicit):
pass
class Browser(Base):
def __getattr__(self, name):
try:
return self._d[name]
except KeyError:
raise AttributeError, name
class values:
def len(self):
return 1
def __getitem__(self, i):
try:
return self._d[i]
except AttributeError:
pass
self._d = self._f()
return self._d[i]
class TableBrowser(Browser, Acquisition.Implicit):
icon = 'what'
Description = check = ''
info = HTMLFile('table_info', globals())
menu = HTMLFile('table_menu', globals())
def tpValues(self):
v = values()
v._f = self.tpValues_
return v
def tpValues_(self):
r=[]
tname=self.__name__
for d in self._c.columns(tname):
b=ColumnBrowser()
b._d=d
try: b.icon=field_icons[d['Type']]
except: pass
b.TABLE_NAME=tname
r.append(b)
return r
def tpId(self): return self._d['TABLE_NAME']
def tpURL(self): return "Table/%s" % self._d['TABLE_NAME']
def Name(self): return self._d['TABLE_NAME']
def Type(self): return self._d['TABLE_TYPE']
manage_designInput=HTMLFile('designInput',globals())
def manage_buildInput(self, id, source, default, REQUEST=None):
"Create a database method for an input form"
args=[]
values=[]
names=[]
columns=self._columns
for i in range(len(source)):
s=source[i]
if s=='Null': continue
c=columns[i]
d=default[i]
t=c['Type']
n=c['Name']
names.append(n)
if s=='Argument':
values.append("<dtml-sqlvar %s type=%s>'" %
(n, vartype(t)))
a='%s%s' % (n, boboType(t))
if d: a="%s=%s" % (a,d)
args.append(a)
elif s=='Property':
values.append("<dtml-sqlvar %s type=%s>'" %
(n, vartype(t)))
else:
if isStringType(t):
if find(d,"\'") >= 0: d=join(split(d,"\'"),"''")
values.append("'%s'" % d)
elif d:
values.append(str(d))
else:
raise ValueError, (
'no default was given for <em>%s</em>' % n)
class ColumnBrowser(Browser):
icon='field'
def check(self):
return ('\t<input type=checkbox name="%s.%s">' %
(self.TABLE_NAME, self._d['Name']))
def tpId(self): return self._d['Name']
def tpURL(self): return "Column/%s" % self._d['Name']
def Description(self):
d=self._d
if d['Scale']:
return " %(Type)s(%(Precision)s,%(Scale)s) %(Nullable)s" % d
else:
return " %(Type)s(%(Precision)s) %(Nullable)s" % d
table_icons={
'TABLE': 'table',
'VIEW':'view',
'SYSTEM_TABLE': 'stable',
}
field_icons={
NUMBER.name: 'i',
STRING.name: 'text',
DATETIME.name: 'date',
INTEGER.name: 'int',
FLOAT.name: 'float',
BOOLEAN.name: 'bin',
ROWID.name: 'int'
}

View File

@ -1,29 +0,0 @@
# ZPsycopgDA/__init__.py - ZPsycopgDA Zope product
#
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 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 Lesser General Public
# License for more details.
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
__doc__ = "ZPsycopg Database Adapter Registration."
__version__ = '2.0'
import DA
def initialize(context):
context.registerClass(
DA.Connection,
permission = 'Add Z Psycopg 2 Database Connections',
constructors = (DA.manage_addZPsycopgConnectionForm,
DA.manage_addZPsycopgConnection),
icon = 'icons/DBAdapterFolder_icon.gif')

View File

@ -1,209 +0,0 @@
# ZPsycopgDA/db.py - query execution
#
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 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 Lesser General Public
# License for more details.
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
from Shared.DC.ZRDB.TM import TM
from Shared.DC.ZRDB import dbi_db
from ZODB.POSException import ConflictError
import site
import pool
import psycopg2
from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE, TIME
from psycopg2.extensions import TransactionRollbackError, register_type
from psycopg2 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, typecasts, enc='utf-8'):
self.dsn = dsn
self.tilevel = tilevel
self.typecasts = typecasts
if enc is None or enc == "":
self.encoding = "utf-8"
else:
self.encoding = enc
self.failures = 0
self.calls = 0
self.make_mappings()
def getconn(self, init=True):
# if init is False we are trying to get hold on an already existing
# connection, so we avoid to (re)initialize it risking errors.
conn = pool.getconn(self.dsn)
if init:
# use set_session where available as in these versions
# set_isolation_level generates an extra query.
if psycopg2.__version__ >= '2.4.2':
conn.set_session(isolation_level=int(self.tilevel))
else:
conn.set_isolation_level(int(self.tilevel))
conn.set_client_encoding(self.encoding)
for tc in self.typecasts:
register_type(tc, conn)
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(False)
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
def make_mappings(self):
"""Generate the mappings used later by self.convert_description()."""
self.type_mappings = {}
for t, s in [(INTEGER,'i'), (LONGINTEGER, 'i'), (NUMBER, 'n'),
(BOOLEAN,'n'), (ROWID, 'i'),
(DATETIME, 'd'), (DATE, 'd'), (TIME, 'd')]:
for v in t.values:
self.type_mappings[v] = (t, s)
def convert_description(self, desc, use_psycopg_types=False):
"""Convert DBAPI-2.0 description field to Zope format."""
items = []
for name, typ, width, ds, p, scale, null_ok in desc:
m = self.type_mappings.get(typ, (STRING, 's'))
items.append({
'name': name,
'type': use_psycopg_types and m[0] or m[1],
'width': width,
'precision': p,
'scale': scale,
'null': null_ok,
})
return items
## 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 ()
self.putconn()
return self.convert_description(c.description, True)
## 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]:
try:
if query_data:
c.execute(qs, query_data)
else:
c.execute(qs)
except TransactionRollbackError:
# Ha, here we have to look like we are the ZODB raising conflict errrors, raising ZPublisher.Publish.Retry just doesn't work
#logging.debug("Serialization Error, retrying transaction", exc_info=True)
raise ConflictError("TransactionRollbackError from psycopg2")
except psycopg2.OperationalError:
#logging.exception("Operational error on connection, closing it.")
try:
# Only close our connection
self.putconn(True)
except:
#logging.debug("Something went wrong when we tried to close the pool", exc_info=True)
pass
if c.description is not None:
nselects += 1
if c.description != desc and nselects > 1:
raise psycopg2.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
return self.convert_description(desc), res

View File

@ -1,108 +0,0 @@
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add Z Psycopg 2 Database Connection',
help_product='ZPsycopgDA',
help_topic='ZPsycopgDA-Method-Add.stx'
)">
<p class="form-help">
A Zope Psycopg 2 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="Psycopg2_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 2 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="4">Read uncommitted</option>
<option value="1">Read committed</option>
<option value="2" selected="YES">Repeatable read</option>
<option value="3">Serializable</option>
</select>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Encoding
</div>
</td>
<td align="left" valign="top">
<input type="text" name="encoding" size="40" value="" />
</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>

View File

@ -1,11 +0,0 @@
<html>
<head><title><dtml-var title_or_id >tables</title></head>
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555" alink="#77003B">
<dtml-var manage_tabs>
<dtml-tree header="info">
<IMG SRC="<dtml-var SCRIPT_NAME >/misc_/ZPsycopgDA/<dtml-var icon>"
ALT="<dtml-var Type>" BORDER="0">
<dtml-var Name><dtml-var Description>
</dtml-tree>
</body>
</html>

View File

@ -1,84 +0,0 @@
<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="4"
<dtml-if expr="tilevel==4">selected="YES"</dtml-if>>
Read uncommitted</option>
<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>>
Repeatable read</option>
<option value="3"
<dtml-if expr="tilevel==3">selected="YES"</dtml-if>>
Serializable</option>
</select>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Encoding
</div>
</td>
<td align="left" valign="top">
<input type="text" name="encoding" size="40"
value="&dtml-encoding;" />
</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>

View File

@ -1,7 +0,0 @@
<dtml-var standard_html_header>
<dtml-var TABLE_TYPE><dtml-if TABLE_OWNER>
owned by <dtml-var TABLE_OWNER></dtml-if>
<dtml-if REMARKS><br><dtml-var REMARKS></dtml-if>
<dtml-var standard_html_footer>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 894 B

View File

@ -1,193 +0,0 @@
# ZPsycopgDA/pool.py - ZPsycopgDA Zope product: connection pooling
#
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 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 Lesser General Public
# License for more details.
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
# All the connections are held in a pool of pools, directly accessible by the
# ZPsycopgDA code in db.py.
import threading
import psycopg2
from psycopg2.pool import PoolError
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._rused = {} # id(conn) -> key map
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 = psycopg2.connect(*self._args, **self._kwargs)
if key is not None:
self._used[key] = conn
self._rused[id(conn)] = key
else:
self._pool.append(conn)
return conn
def _getkey(self):
"""Return a new unique key."""
self._keys += 1
return self._keys
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 key in self._used:
return self._used[key]
if self._pool:
self._used[key] = conn = self._pool.pop()
self._rused[id(conn)] = key
return conn
else:
if len(self._used) == self.maxconn:
raise PoolError("connection pool exausted")
return self._connect(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._rused[id(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]
del self._rused[id(conn)]
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:
conn.close()
except:
pass
self.closed = True
class PersistentConnectionPool(AbstractConnectionPool):
"""A pool that assigns persistent connections to different threads.
Note that this connection pool generates by itself the required keys
using the current thread id. This means that until a thread puts away
a connection it will always get the same connection object by successive
`!getconn()` calls. This also means that a thread can't use more than one
single connection from the pool.
"""
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()
_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] = \
PersistentConnectionPool(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)

View File

@ -1,7 +0,0 @@
ZPsycopgDA (in the Debian zope-psycopgda package) is a Zope Database
Adapter based on the psycopg Python/PostgreSQL driver. You'll find
more information and documentation in the pythonX.Y-psycopg package,
where X.Y is the version of your installed Python.
Details for ZPsycopgDA for Zope are found in the documentation of
the python2.3-psycopg package.

676
debian/changelog vendored
View File

@ -1,676 +0,0 @@
psycopg2 (2.0.7-2) unstable; urgency=medium
* ZPsycopgDA/DA.py: updated the patch. (Closes: #478860)
-- Fabio Tranchitella <kobold@debian.org> Thu, 01 May 2008 17:43:54 +0200
psycopg2 (2.0.7-1) unstable; urgency=low
* New upstream release. (Closes: #476101)
* debian/control: bumped Standard-Versions to 3.7.3.
-- Fabio Tranchitella <kobold@debian.org> Tue, 15 Apr 2008 10:05:51 +0200
psycopg2 (2.0.6-4) unstable; urgency=low
[ Sandro Tosi ]
* debian/control
- uniforming Vcs-Browser field
[ Fabio Tranchitella ]
* Provides a encoding parameter when adding a ZPsycopgDA instance using the
ZMI. (Closes: #475123)
-- Fabio Tranchitella <kobold@debian.org> Wed, 09 Apr 2008 19:51:10 +0200
psycopg2 (2.0.6-3) unstable; urgency=low
[ Piotr Ożarowski ]
* XS-Vcs-Svn field renamed to Vcs-Svn
* Vcs-Browser field added
[ Fabio Tranchitella ]
* Mention DB-API 2.0 compatibility in the long description.
(Closes: #430763)
-- Fabio Tranchitella <kobold@debian.org> Thu, 08 Nov 2007 15:07:05 +0100
psycopg2 (2.0.6-2) unstable; urgency=low
* Build a python-psycopg-dbg package
-- Scott Kitterman <scott@kitterman.com> Tue, 03 Jul 2007 16:55:48 -0400
psycopg2 (2.0.6-1) unstable; urgency=low
* New upstream release.
* psycopg2da: removed, merged upstream.
-- Fabio Tranchitella <kobold@debian.org> Sat, 09 Jun 2007 22:38:23 +0200
psycopg2 (2.0.5.1-7) UNRELEASED; urgency=low
* debian/watch: added.
-- Fabio Tranchitella <kobold@debian.org> Fri, 9 Feb 2007 12:35:55 +0100
psycopg2 (2.0.5.1-6) unstable; urgency=high
* debian/zope-psycopgda2.dzproduct: requires Zope 2.9 or higher: previous
versions use python2.3 which is not supported anymore in psycopg.
-- Fabio Tranchitella <kobold@debian.org> Mon, 15 Jan 2007 11:39:15 +0100
psycopg2 (2.0.5.1-5) unstable; urgency=medium
* Do not run dh_pycentral on zope-psycopgda2. (Closes: #400846)
-- Fabio Tranchitella <kobold@debian.org> Wed, 29 Nov 2006 09:04:09 +0100
psycopg2 (2.0.5.1-4) unstable; urgency=medium
* Fixed a bug in psycopg2da.
* debian/control: bumped build-dependency on zope-debhelper.
* Added XS-Vcs-Svn field
-- Fabio Tranchitella <kobold@debian.org> Fri, 24 Nov 2006 13:50:11 +0100
psycopg2 (2.0.5.1-3) unstable; urgency=low
* psycopgda: imported upstream psycopg2da database adapter from SVN, which
builds a new binary package, python-psycopg2da.
-- Fabio Tranchitella <kobold@debian.org> Fri, 10 Nov 2006 08:56:05 +0100
psycopg2 (2.0.5.1-2) unstable; urgency=low
* debian/control: added again a dependency on python-egenix-mxdatetime.
(Closes: #389636)
-- Fabio Tranchitella <kobold@debian.org> Tue, 3 Oct 2006 10:25:22 +0200
psycopg2 (2.0.5.1-1) unstable; urgency=low
* New upstream release.
-- Fabio Tranchitella <kobold@debian.org> Tue, 19 Sep 2006 08:22:36 +0200
psycopg2 (2.0.4-1) unstable; urgency=low
* New upstream release.
* debian/control:
+ removed dependency on python-egenix-mxdatetime.
+ added ${shlibs:Depends} for the python-psycopg2 package.
(Closes: #381462)
-- Fabio Tranchitella <kobold@debian.org> Wed, 9 Aug 2006 10:28:30 +0200
psycopg2 (2.0.2-1) unstable; urgency=low
* New upstream major release, new source package. (Closes: #377956)
-- Fabio Tranchitella <kobold@debian.org> Sun, 16 Jul 2006 21:43:41 +0200
psycopg (1.1.21-8) unstable; urgency=low
* debian/zope-psycopgda.dzproduct: added 2.9 to the list of supported
zope versions. (Closes: #376538)
-- Fabio Tranchitella <kobold@debian.org> Fri, 14 Jul 2006 10:19:54 +0200
psycopg (1.1.21-7) unstable; urgency=low
* Moved dh_installzope within an arch-indep target. (Closes: #373842)
-- Fabio Tranchitella <kobold@debian.org> Fri, 16 Jun 2006 09:37:23 +0200
psycopg (1.1.21-6) unstable; urgency=low
* Python policy transition. (Closes: #373482)
-- Fabio Tranchitella <kobold@debian.org> Thu, 15 Jun 2006 19:09:36 +0200
psycopg (1.1.21-5) unstable; urgency=high
* ypemod.c, new_psyco_bufferobject():
- Escape quotes psycopg.Binary() results as '', not as \', since the
latter does not work any more with some client encodings with the latest
PostgreSQL (in some multi-byte encodings you can exploit \' escaping to
inject SQL code, see CVE-2006-2314). (Closes: #369230)
Thanks to Martin Pitt and Ubuntu security team for the patch.
-- Fabio Tranchitella <kobold@debian.org> Tue, 30 May 2006 22:15:06 +0200
psycopg (1.1.21-4) unstable; urgency=low
* debian/rules: remove *.o in the clean target. (Closes: #352835)
-- Fabio Tranchitella <kobold@debian.org> Thu, 16 Feb 2006 12:06:53 +0000
psycopg (1.1.21-3) unstable; urgency=low
* debian/control: removed build-dependency on postgresql-server-dev-8.0,
as suggested by Martin Pitt. (Closes: #339640)
-- Fabio Tranchitella <kobold@debian.org> Fri, 18 Nov 2005 08:44:26 +0000
psycopg (1.1.21-2) unstable; urgency=low
* debian/control: zope-psycopgda should depend on the same version of the
psycopg python module. (Closes: #336765)
-- Fabio Tranchitella <kobold@debian.org> Wed, 2 Nov 2005 12:07:33 +0000
psycopg (1.1.21-1) unstable; urgency=low
* New maintainer; Thanks Federico for your work, and be sure that I'll
take care of this package.
* New upstream release (Closes: #321592, #320618, #333638)
* debian/python2.4-psycopg.dirs: added. (Closes: #319509, #329115)
* debian/control: dropped support for python2.1 and
python2.2. (Closes: #333639)
* debian/control: Standards-Version bumped to 3.6.2, no changes required.
* debian/rules: make use of dh_installzope from zope-debhelper to build the
zope-psycopgda package.
(Closes: #158669, #323599, #268975, #292247, #327415)
* debian/control: added build-depends on postgresql-server-dev-8.0.
(Closes: #333638)
* Re-packaged upstream tarball replacing some broken images.
(Closes: #292008, #305392)
-- Fabio Tranchitella <kobold@debian.org> Fri, 28 Oct 2005 11:24:37 +0000
psycopg (1.1.19-1) unstable; urgency=low
* New upstream release.
* Applied patch from Martin Krafft to build Zope 2.7 packages.
* Modified to use the new PostgreSQL packages.
* Added python 2.4 package (Closes: #301403).
* Upstream applied various Ubuntu patches (Closes: #301947, #300893).
-- Federico Di Gregorio <fog@debian.org> Sat, 16 Jul 2005 20:47:08 +0200
psycopg (1.1.18-1) unstable; urgency=low
* New upstream release.
* 1.1.16 fixed rowcount bug (closes: #266299).
-- Federico Di Gregorio <fog@debian.org> Wed, 5 Jan 2005 21:05:15 +0100
psycopg (1.1.17-1) unstable; urgency=high
* Urgency is still high because 1.1.16 was never uploaded.
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Thu, 19 Nov 2004 01:14:30 +0200
psycopg (1.1.16-1) unstable; urgency=high
* New upstream release.
* Tagged with urgency=high because fix a grave bug (rowcount) introduced
in 1.1.15.
* Upstream fix: does not segfault when using COPY TO/COPY FROM in
.execute() (closes: #279222).
-- Federico Di Gregorio <fog@debian.org> Sat, 30 Oct 2004 02:35:30 +0200
psycopg (1.1.15-1) unstable; urgency=low
* New upstream release.
* Definitely fixed (ah ah) time interval problems (closes: #259213).
-- Federico Di Gregorio <fog@initd.org> Thu, 29 Jul 2004 23:43:59 +0200
psycopg (1.1.14-1) unstable; urgency=low
* New upstream release.
* Don't put two copies of changelog in every package anymore
(closes: #256662).
* Updated test script works as expected (closes: #231391).
* Changes from NMU incorporated in 1.1.12:
- zpsycopgda depends on python2.2-psycopg (closes: #227420, #227147).
- compiled with postgresql in unstable (close: #220527).
-- Federico Di Gregorio <fog@initd.org> Fri, 9 Jul 2004 23:01:40 +0200
psycopg (1.1.13-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Fri, 21 May 2004 10:33:54 +0200
psycopg (1.1.12-1) unstable; urgency=low
* New upstream release (the "martin you won't have this package"
release.)
* Integrated changes from NMU releases.
-- Federico Di Gregorio <fog@debian.org> Sun, 16 May 2004 10:14:47 +0200
psycopg (1.1.10-1.2) unstable; urgency=low
* Non-maintainer upload. Thinking about taking this package over...
* Changed dependency on pyscopgda Python module to Python version 2.2.
(closes: #227147, #227420)
* Added Lintian overrides for image-in-/usr/lib warnings -- Zope needs
these images...
-- martin f. krafft <madduck@debian.org> Thu, 15 Apr 2004 23:30:40 +0200
psycopg (1.1.10-1.1) unstable; urgency=low
* Non-maintainer upload.
* No changes - this upload is simply a rebuild against the current unstable
instead of experimental postgresql-dev.
(closes: #219927, #220141, #220173, #220527)
-- Peter Hawkins <peterh@debian.org> Sun, 28 Dec 2003 10:57:30 +1100
psycopg (1.1.10-1) unstable; urgency=low
* Added download location to debian/copyright file (Closes: #215880).
-- Federico Di Gregorio <fog@initd.org> Sat, 8 Nov 2003 23:32:40 +0100
psycopg (1.1.9-1) unstable; urgency=low
* New upstream release.
* Bug was agains an old 1.0.x version of psycopg (Closes: #208702).
-- Federico Di Gregorio <fog@initd.org> Wed, 10 Sep 2003 13:04:42 +0200
psycopg (1.1.8-1) unstable; urgency=low
* New upstream release.
* Integrated NMU from Matthias Klose (closes: #205746).
-- Federico Di Gregorio <fog@debian.org> Fri, 1 Aug 2003 11:50:57 +0200
psycopg (1.1.5.1-1.1) unstable; urgency=low
* NMU
* Update for python2.3 as the default python version (closes: #205746).
-- Matthias Klose <doko@debian.org> Fri, 22 Aug 2003 00:02:25 +0200
psycopg (1.1.7-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Sat, 26 Jul 2003 15:03:39 +0200
psycopg (1.1.6-1) unstable; urgency=low
* New upstream release.
* Upstream applied patch from BTS (Closes: #200161).
-- Federico Di Gregorio <fog@initd.org> Sun, 13 Jul 2003 23:36:04 +0200
psycopg (1.1.5.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@initd.org> Mon, 23 Jun 2003 00:37:33 +0200
psycopg (1.1.5-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@initd.org> Sun, 22 Jun 2003 21:30:01 +0200
psycopg (1.1.4-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Wed, 7 May 2003 15:21:31 +0200
psycopg (1.1.3-1) unstable; urgency=low
* New upstream release.
* Changed section in debian/control (-> python).
-- Federico Di Gregorio <fog@debian.org> Wed, 2 Apr 2003 10:33:36 +0200
psycopg (1.1.2-1) unstable; urgency=low
* New upstream release.
* Started to track the 1.1.x branch.
-- Federico Di Gregorio <fog@debian.org> Tue, 25 Feb 2003 01:06:08 +0100
psycopg (1.0.15.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Fri, 14 Feb 2003 16:09:50 +0100
psycopg (1.0.15-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Wed, 12 Feb 2003 23:49:51 +0100
psycopg (1.0.14-1) unstable; urgency=low
* Applied patch from John Goerzen to fix memory leak in executemany()
and callproc() (Closes: #169284).
* New upstream release (Closes: #170297).
-- Federico Di Gregorio <fog@debian.org> Mon, 25 Nov 2002 16:50:37 +0100
psycopg (1.0.13-1) unstable; urgency=low
* New upstream release.
* Python 2.3 package added (Closes: #160831)
* IntegrityError raised when needed (upstream, Closes: #165791)
* Packages are lintian clean again.
-- Federico Di Gregorio <fog@debian.org> Fri, 25 Oct 2002 11:54:19 +0200
psycopg (1.0.12-1) unstable; urgency=low
* New upstream release.
* Fixed wrong url in RELEASE-1.0. (Closes: #153840)
-- Federico Di Gregorio <fog@debian.org> Fri, 13 Sep 2002 13:16:36 +0200
psycopg (1.0.11.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Mon, 26 Aug 2002 10:41:54 +0200
psycopg (1.0.11-1) unstable; urgency=low
* New upstream release.
* The dummy python-psycopg package now depends on the new default debian
python (2.2) and on python2.2-psycopg.
* Removed support for python 1.5 (support for 2.3 has to wait for egenix
packages.)
-- Federico Di Gregorio <fog@debian.org> Fri, 23 Aug 2002 11:25:01 +0200
psycopg (1.0.10-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@initd.org> Mon, 22 Jul 2002 02:04:59 +0200
psycopg (1.0.9-1) unstable; urgency=low
* Resolved section override (main->interpreters).
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Thu, 20 Jun 2002 14:00:42 +0200
psycopg (1.0.8-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Tue, 23 Apr 2002 22:42:22 +0200
psycopg (1.0.7.1-2) unstable; urgency=low
* Moved to main.
-- Federico Di Gregorio <fog@debian.org> Fri, 19 Apr 2002 10:06:58 +0200
psycopg (1.0.7.1-1) unstable; urgency=low
* New upstream release.
* Fixed a bug in ./configure; closes: #141774.
-- Federico Di Gregorio <fog@debian.org> Mon, 8 Apr 2002 18:54:24 +0200
psycopg (1.0.7-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Fri, 29 Mar 2002 14:24:45 +0100
psycopg (1.0.6-1) unstable; urgency=low
* New upstream release.
* Builds with new libpq libraries and header layout.
-- Federico Di Gregorio <fog@debian.org> Thu, 7 Mar 2002 11:59:40 +0100
psycopg (1.0.5-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Mon, 4 Mar 2002 14:43:13 +0100
psycopg (1.0.4-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Wed, 20 Feb 2002 20:37:16 +0100
psycopg (1.0.3-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Fri, 8 Feb 2002 15:17:44 +0100
psycopg (1.0.2-1) unstable; urgency=low
* New upstream release.
* Added package for python2.2 (Closes: #132650).
-- Federico Di Gregorio <fog@debian.org> Fri, 8 Feb 2002 00:45:07 +0100
psycopg (1.0.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Sun, 20 Jan 2002 18:27:22 +0100
psycopg (1.0-4) unstable; urgency=low
* Added build depend on plain python, to really close the %£$! #121229
bug this time (Closes: #121229).
-- Federico Di Gregorio <fog@debian.org> Wed, 28 Nov 2001 10:50:06 +0100
psycopg (1.0-3) unstable; urgency=low
* Added explicit build depends on python 1.5 & 2.1 (Closes: #121229).
* Fixed bad dependency on python1.5-egenix-mxdatetime.
-- Federico Di Gregorio <fog@debian.org> Mon, 26 Nov 2001 17:18:41 +0100
psycopg (1.0-2) unstable; urgency=low
* Fixed dependencies as per python policy.
* Added default, unversioned psycopg package (python-psycopg).
* Added non-US/main and rebuilt after REJECT.
-- Federico Di Gregorio <fog@debian.org> Fri, 16 Nov 2001 01:14:54 +0100
psycopg (1.0-1) unstable; urgency=low
* New upstream release. 1.0!
* Now we build versioned packages for python 1.5 and 2.1.
-- Federico Di Gregorio <fog@debian.org> Tue, 13 Nov 2001 19:24:39 +0100
psycopg (0.99.7-1) unstable; urgency=low
* New upstream release fixing some little bugs.
* This version requires the mx DateTime packages that are not yet in
debian... waiting for them I'll distribute both psycopg and unofficial
packages on the initd psycopg page.
-- Federico Di Gregorio <fog@debian.org> Tue, 18 Sep 2001 23:28:51 +0200
psycopg (0.99.6-2) unstable; urgency=low
* Added suggested build-depends (Closes: #112112).
* Applied patch by Michael Weber to configure.in, to look for a compiler
(Closes: #112024).
-- Federico Di Gregorio <fog@debian.org> Thu, 13 Sep 2001 10:49:37 +0200
psycopg (0.99.6-1) unstable; urgency=low
* Added Build-depends line (Closes: #89798).
* Now zope-psycopgda requires python-psycopg, zope on debian still runs
with python 1.x only (Closes: #108890).
* Moved package to non-US (psycopg depends on postgresql that is in
non-US, sic).
-- Federico Di Gregorio <fog@debian.org> Mon, 3 Sep 2001 13:02:11 +0200
psycopg (0.99.5-1) unstable; urgency=low
* New upstream release with bound variables quoting (Closes: #102843).
* The build process set the correct path to DateTime module
(Closes: #102838).
* Removes .pyc files in prerm (Closes: #104382)
-- Federico Di Gregorio <fog@debian.org> Thu, 12 Jul 2001 12:56:38 +0200
psycopg (0.99.4-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Mon, 2 Jul 2001 15:33:29 +0200
psycopg (0.99.3-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Wed, 20 Jun 2001 12:55:47 +0200
psycopg (0.99.2-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Tue, 5 Jun 2001 15:37:50 +0200
psycopg (0.99.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Tue, 5 Jun 2001 12:46:18 +0200
psycopg (0.5.5-1) unstable; urgency=low
* New upstream release (ok, *we* are the upstream authors, but after
putting the -1 in the version i am supposed to say "new upstream
version" when the non-debian versions changes, right? ouch...)
-- Federico Di Gregorio <fog@debian.org> Fri, 1 Jun 2001 17:18:52 +0200
psycopg (0.5.4-1) unstable; urgency=low
* Another bugfixing release.
* Added debian revision to be able to release multiple versions with the
same upstream version.
-- Federico Di Gregorio <fog@debian.org> Fri, 18 May 2001 19:32:59 +0200
psycopg (0.5.3) unstable; urgency=low
* Some bugs fixed, new release.
-- Federico Di Gregorio <fog@debian.org> Fri, 4 May 2001 16:19:09 +0200
psycopg (0.5.2) unstable; urgency=low
* New bugfixing release.
-- Federico Di Gregorio <fog@debian.org> Fri, 27 Apr 2001 09:52:16 +0200
psycopg (0.5.1) unstable; urgency=low
* New bugfixing release.
-- Federico Di Gregorio <fog@debian.org> Tue, 3 Apr 2001 11:13:26 +0200
psycopg (0.5.0) unstable; urgency=low
* New release.
-- Federico Di Gregorio <fog@debian.org> Fri, 30 Mar 2001 12:54:42 +0200
psycopg (0.4.7) unstable; urgency=low
* New release.
* Lots of small bug fixes (see detailed ChangeLog.)
* Includes beginning of DBAPI-2.0 testsuite.
-- Federico Di Gregorio <fog@debian.org> Fri, 16 Mar 2001 18:29:03 +0100
psycopg (0.4.6) unstable; urgency=low
* New release.
* Fixed a little bug in debian/rules (does not create an examples
directory inside examples.)
-- Federico Di Gregorio <fog@debian.org> Wed, 14 Mar 2001 01:00:26 +0100
psycopg (0.4.5) unstable; urgency=low
* New upstream (mmm... but one of the upstream authors it is
*me*... mmm...) release.
-- Federico Di Gregorio <fog@debian.org> Mon, 12 Mar 2001 11:41:42 +0100
psycopg (0.4.4) unstable; urgency=low
* New release.
* Fixed Sections in debian/control.
-- Federico Di Gregorio <fog@debian.org> Fri, 9 Mar 2001 10:11:02 +0100
psycopg (0.4.3) unstable; urgency=low
* New release.
* Fixed typo in connectionAdd.dtml (Closes: #88817)
-- Federico Di Gregorio <fog@debian.org> Wed, 7 Mar 2001 15:54:35 +0100
psycopg (0.4.2) unstable; urgency=low
* New release (fixes bugs in ZPsycopgDA.)
-- Federico Di Gregorio <fog@debian.org> Mon, 5 Mar 2001 13:33:39 +0100
psycopg (0.4.1) unstable; urgency=low
* New release.
* we now create packages for both versions of python in debian
(1.5 and 2.0, packages python-* and python2-*)
-- Federico Di Gregorio <fog@debian.org> Fri, 2 Mar 2001 12:10:52 +0100
psycopg (0.4) unstable; urgency=low
* News release.
* Now debian/rules build the Zope Database Adapter zope-psycopgda too.
* Source name changed from python-psycopg to psycopg.
-- Federico Di Gregorio <fog@debian.org> Tue, 27 Feb 2001 15:11:04 +0100
python-psycopg (0.3) unstable; urgency=low
* New release. Tons of bugs fixed and new features, see ChangeLog for
details.
-- Federico Di Gregorio <fog@debian.org> Mon, 26 Feb 2001 21:22:23 +0100
python-psycopg (0.2) unstable; urgency=low
* New release. Fixed lots of bugs and memory leaks.
-- Federico Di Gregorio <fog@debian.org> Fri, 16 Feb 2001 11:04:17 +0100
python-psycopg (0.1) unstable; urgency=low
* Initial release.
-- Federico Di Gregorio <fog@debian.org> Mon, 12 Feb 2001 14:46:53 +0100

1
debian/compat vendored
View File

@ -1 +0,0 @@
5

64
debian/control vendored
View File

@ -1,64 +0,0 @@
Source: psycopg2
Section: python
Priority: optional
Build-Depends: debhelper (>= 5.0.37.2), python-all-dev, python-all-dbg, python-central (>= 0.5.0), python (>= 2.3.5-7), python-egenix-mx-base-dev, autoconf, libpq-dev
Build-Depends-Indep: zope-debhelper (>= 0.3.4)
Maintainer: Fabio Tranchitella <kobold@debian.org>
Standards-Version: 3.7.3
XS-Python-Version: all
Vcs-Svn: svn://svn.debian.org/python-modules/packages/psycopg2/trunk/
Vcs-Browser: http://svn.debian.org/wsvn/python-modules/packages/psycopg2/trunk/?op=log
Package: python-psycopg2
Architecture: any
Section: python
Depends: ${python:Depends}, ${shlibs:Depends}, python-egenix-mxdatetime
Provides: ${python:Provides}
XB-Python-Version: ${python:Versions}
Description: Python module for PostgreSQL
psycopg is a PostgreSQL database adapter for the Python programming language
(just like pygresql and popy.) 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 provides full asycronous operations for the really brave
programmer.
.
The main advantages of psycopg2 are that it supports the full Python DBAPI-2.0
and being thread safe at level 2. It also includes some extensions to the
standard DBAPI-2.0 to allow for better thread performance.
Package: python-psycopg2-dbg
Priority: extra
Architecture: any
Section: python
Depends: python-psycopg2 (= ${binary:Version}), python-dbg, python-egenix-mxdatetime-dbg, ${shlibs:Depends}
Description: Python module for PostgreSQL (debug extension)
psycopg is a PostgreSQL database adapter for the Python programming language
(just like pygresql and popy.) 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.
.
This package contains the extensions built for the Python debug interpreter.
Package: zope-psycopgda2
Architecture: all
Section: python
Depends: ${zope:Depends}, python-psycopg2 (>= ${source:Version})
Description: Zope database adapter based on python-psycopg2
The package contains the PostgreSQL database adapter for Zope 2.7, 2.8 and
2.9 based on the psycopg2 Python module.
Package: python-psycopg2da
Architecture: all
Section: python
Depends: ${zope:Depends}, python-psycopg2 (>= ${source:Version})
XB-Python-Version: ${zope:PythonVersion}
Description: Zope database adapter based on python-psycopg2 -- zope3 version
The package contains the PostgreSQL database adapter for Zope 3 based on
the psycopg2 Python module.

112
debian/copyright vendored
View File

@ -1,112 +0,0 @@
This package was debianized by Fabio Tranchitella <kobold@debian.org> on
Sun, 16 Jul 2006 21:10:01 +0200.
psycopg2 can be downloaded from its homepage:
http://initd.org/projects/psycopg
The tarball has been re-packed to get rid of the upstream debian/ directory:
no other changes have been made to the tarball.
Copyright:
Copyright (C) 2001-2006 Federico Di Gregorio <fog@debian.org>
Copyright (C) 2001 Michele Comitini <mcm@initd.org>
For psycopg2da:
Copyright (C) 2006 Fabio Tranchitella <kobold@debian.org>
For the files doc/copy_from.py and doc/copy_to.py:
Copyright (C) 2001-2005 Federico Di Gregorio <fog@debian.org>
Copyright (C) 2002 Tom Jenkins <tjenkins@devis.com>
For the file tests/dbapi20.py:
Copyright (C) 2003 Ian Bicking <ianb@colorstudy.com>
For the file scripts/ext2html.py:
Copyright (C) 2003 Daniele Varrazzo <daniele.varrazzo@gmail.com>
License for psycopg2, ZPsycopgDA and psycopg2da:
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.
On Debian GNU/Linux systems, the complete text of the GNU General
Public License can be found in '/usr/share/common-licenses/GPL'.
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).
Note that the GPL was chosen to avoid proprietary adapters based on
psycopg code. Using psycopg in a proprietary product (even bundling
psycopg with the proprietary product) is fine as long as:
1. psycopg is called from Python only using only the provided API
(i.e., no linking with C code and no C modules based on it); and
2. all the other points of the GPL are respected (you offer a copy
of psycopg's source code, and so on.)
License for the files tests/dbapi20.py and scripts/ext2html.py:
These modules have been placed in the public domain.
Alternative licenses for ZPsycopgDA:
If you prefer you can use the Zope Database Adapter ZPsycopgDA (i.e.,
every file inside the ZPsycopgDA directory) user the ZPL license as
published on the Zope web site, http://www.zope.org/Resources/ZPL.
Alternative licenses for psycopg2da:
If you prefer you can use the Zope3 Database Adapter psycopg2da (i.e.,
every file inside the psycopg2da directory) user the ZPL license as
published on the Zope web site, http://www.zope.org/Resources/ZPL.
Alternative licenses for psycopg/adapter*.{j,c} and
psycopg/microprotocol*.{h.c}:
Also, the following BSD-like license applies (at your option) to the
files following the pattern psycopg/adapter*.{h,c} and
psycopg/microprotocol*.{h,c}:
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
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.
Proprietary licenses:
A non-exclusive license is available for companies that want to include
psycopg in their proprietary products without respecting the spirit of the
GPL. The price of the license is one day of development done by the author,
at the consulting fee he applies to his usual customers at the day of the
request.
Please contact the upstream author (Federico Di Gregorio <fog@debian.org>)
for more information about this license.

1
debian/pycompat vendored
View File

@ -1 +0,0 @@
2

View File

@ -1,3 +0,0 @@
Name: psycopg2da
ZopeVersions: 3
Global: yes

93
debian/rules vendored
View File

@ -1,93 +0,0 @@
#!/usr/bin/make -f
# Sample debian/rules that uses debhelper.
# GNU copyright 1997 to 1999 by Joey Hess.
PYVERS=$(shell pyversions -r debian/control)
configure: configure-stamp
configure-stamp:
dh_testdir
rm -f configure
touch configure-stamp
build: configure build-stamp
build-stamp:
dh_testdir
for python in $(PYVERS); do \
$$python setup.py build ; \
done
for python in $(PYVERS); do \
$$python-dbg setup.py build ; \
done
touch build-stamp
clean: configure
dh_testdir
dh_testroot
rm -fr *-stamp build
for python in $(PYVERS); do \
$$python setup.py clean ; \
done
dh_clean
install-arch: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
# psycopg2
for python in $(PYVERS); do \
$$python setup.py install \
--root=$(CURDIR)/debian/python-psycopg2 --no-compile; \
done
for python in $(PYVERS); do \
$$python-dbg setup.py install \
--root=$(CURDIR)/debian/python-psycopg2-dbg --no-compile; \
done
find debian/python-*-dbg ! -type d ! -name '*.so' | xargs rm -f
find debian/python-*-dbg -depth -empty -exec rmdir {} \;
install-indep: build
# Zope package
dh_installzope -p zope-psycopgda2 ZPsycopgDA
# Zope3 package
dh_installzope -p python-psycopg2da psycopg2da
# Build architecture-independent files here.
binary-indep: build install-indep
dh_testdir
dh_testroot
dh_installdocs -i AUTHORS
dh_installchangelogs -i
dh_link -i
dh_compress -i
dh_fixperms -i
dh_pycentral -p python-psycopg2da
dh_installdeb -i
dh_gencontrol -i
dh_md5sums -i
dh_builddeb -i
# Build architecture-dependent files here.
binary-arch: build install-arch
dh_testdir
dh_testroot
dh_installdocs -a README AUTHORS doc tests
dh_installchangelogs -a ChangeLog
dh_link -a
dh_strip -ppython-psycopg2 --dbg-package=python-psycopg2-dbg
rm -rf debian/python-psycopg2-dbg/usr/share/doc/python-psycopg2-dbg
ln -s python-psycopg2 debian/python-psycopg2-dbg/usr/share/doc/python-psycopg2-dbg
dh_compress -a
dh_fixperms -a
dh_makeshlibs -a
dh_pycentral -a
dh_python -a
dh_installdeb -a
dh_shlibdeps -a
dh_gencontrol -a
dh_md5sums -a
dh_builddeb -a
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install configure

2
debian/watch vendored
View File

@ -1,2 +0,0 @@
version=3
http://www.initd.org/pub/software/psycopg/psycopg2-([0-9][0-9\.\-]*).tar.gz debian uupdate

View File

@ -1,4 +0,0 @@
Name: ZPsycopgDA
Directory: ZPsycopgDA:2
Package: zope-psycopgda2
ZopeVersions: >= 2.9

View File

@ -26,3 +26,7 @@ a > tt.sql:hover {
dl.faq dt { dl.faq dt {
font-weight: bold; font-weight: bold;
} }
table.data-types div.line-block {
margin-bottom: 0;
}

View File

@ -27,6 +27,7 @@ More advanced topics
wait(aconn) wait(aconn)
acurs = aconn.cursor() acurs = aconn.cursor()
.. index:: .. index::
double: Subclassing; Cursor double: Subclassing; Cursor
double: Subclassing; Connection double: Subclassing; Connection
@ -45,6 +46,16 @@ but other uses are possible. `cursor` is much more interesting, because
it is the class where query building, execution and result type-casting into it is the class where query building, execution and result type-casting into
Python variables happens. Python variables happens.
The `~psycopg2.extras` module contains several examples of :ref:`connection
and cursor sublcasses <cursor-subclasses>`.
.. note::
If you only need a customized cursor class, since Psycopg 2.5 you can use
the `~connection.cursor_factory` parameter of a regular connection instead
of creating a new `!connection` subclass.
.. index:: .. index::
single: Example; Cursor subclass single: Example; Cursor subclass
@ -403,13 +414,13 @@ this will be probably implemented in a future release.
.. _green-support: .. _green-support:
Support to coroutine libraries Support for coroutine libraries
------------------------------ -------------------------------
.. versionadded:: 2.2.0 .. versionadded:: 2.2.0
Psycopg can be used together with coroutine_\-based libraries, and participate Psycopg can be used together with coroutine_\-based libraries and participate
to cooperative multithreading. in cooperative multithreading.
Coroutine-based libraries (such as Eventlet_ or gevent_) can usually patch the Coroutine-based libraries (such as Eventlet_ or gevent_) can usually patch the
Python standard library in order to enable a coroutine switch in the presence of Python standard library in order to enable a coroutine switch in the presence of

View File

@ -26,7 +26,7 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.ifconfig',
'sphinx.ext.doctest', 'sphinx.ext.intersphinx' ] 'sphinx.ext.doctest', 'sphinx.ext.intersphinx' ]
# Specific extensions for Psycopg documentation. # Specific extensions for Psycopg documentation.
extensions += [ 'dbapi_extension', 'sql_role' ] extensions += [ 'dbapi_extension', 'sql_role', 'ticket_role' ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']
@ -42,7 +42,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Psycopg' project = u'Psycopg'
copyright = u'2001-2011, Federico Di Gregorio. Documentation by Daniele Varrazzo' copyright = u'2001-2013, Federico Di Gregorio. Documentation by Daniele Varrazzo'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
@ -65,6 +65,9 @@ intersphinx_mapping = {
'py3': ('http://docs.python.org/3.2', None), 'py3': ('http://docs.python.org/3.2', None),
} }
# Pattern to generate links to the bug tracker
ticket_url = 'http://psycopg.lighthouseapp.com/projects/62710/tickets/%s'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
#language = None #language = None

View File

@ -21,16 +21,17 @@ The ``connection`` class
Connections are thread safe and can be shared among many threads. See Connections are thread safe and can be shared among many threads. See
:ref:`thread-safety` for details. :ref:`thread-safety` for details.
.. method:: cursor([name] [, cursor_factory] [, withhold]) .. method:: cursor(name=None, cursor_factory=None, scrollable=None, withhold=False)
Return a new `cursor` object using the connection. Return a new `cursor` object using the connection.
If *name* is specified, the returned cursor will be a :ref:`server If *name* is specified, the returned cursor will be a :ref:`server
side cursor <server-side-cursors>` (also known as *named cursor*). side cursor <server-side-cursors>` (also known as *named cursor*).
Otherwise it will be a regular *client side* cursor. By default a Otherwise it will be a regular *client side* cursor. By default a
:sql:`WITHOUT HOLD` cursor is created; to create a :sql:`WITH HOLD` named cursor is declared without :sql:`SCROLL` option and
cursor, pass a `!True` value as the *withhold* parameter. See :sql:`WITHOUT HOLD`: set the argument or property `~cursor.scrollable`
:ref:`server-side-cursors`. to `!True`/`!False` and or `~cursor.withhold` to `!True` to change the
declaration.
The name can be a string not valid as a PostgreSQL identifier: for The name can be a string not valid as a PostgreSQL identifier: for
example it may start with a digit and contain non-alphanumeric example it may start with a digit and contain non-alphanumeric
@ -46,14 +47,17 @@ The ``connection`` class
Consider it as part of the query, not as a query parameter. Consider it as part of the query, not as a query parameter.
The *cursor_factory* argument can be used to create non-standard The *cursor_factory* argument can be used to create non-standard
cursors. The class returned should be a subclass of cursors. The class returned must be a subclass of
`psycopg2.extensions.cursor`. See :ref:`subclassing-cursor` for `psycopg2.extensions.cursor`. See :ref:`subclassing-cursor` for
details. details. A default factory for the connection can also be specified
using the `~connection.cursor_factory` attribute.
.. versionchanged:: 2.4.3 added the *withhold* argument.
.. versionchanged:: 2.5 added the *scrollable* argument.
.. extension:: .. extension::
The `name` and `cursor_factory` parameters are Psycopg All the function arguments are Psycopg extensions to the |DBAPI|.
extensions to the |DBAPI|.
.. index:: .. index::
@ -71,6 +75,10 @@ The ``connection`` class
automatically open, commands have immediate effect. See automatically open, commands have immediate effect. See
:ref:`transactions-control` for details. :ref:`transactions-control` for details.
.. versionchanged:: 2.5 if the connection is used in a ``with``
statement, the method is automatically called if no exception is
raised in the ``with`` block.
.. index:: .. index::
pair: Transaction; Rollback pair: Transaction; Rollback
@ -81,6 +89,10 @@ The ``connection`` class
connection without committing the changes first will cause an implicit connection without committing the changes first will cause an implicit
rollback to be performed. rollback to be performed.
.. versionchanged:: 2.5 if the connection is used in a ``with``
statement, the method is automatically called if an exception is
raised in the ``with`` block.
.. method:: close() .. method:: close()
@ -493,6 +505,15 @@ The ``connection`` class
the payload was not accessible. To keep backward compatibility, the payload was not accessible. To keep backward compatibility,
`!Notify` objects can still be accessed as 2 items tuples. `!Notify` objects can still be accessed as 2 items tuples.
.. attribute:: cursor_factory
The default cursor factory used by `~connection.cursor()` if the
parameter is not specified.
.. versionadded:: 2.5
.. index:: .. index::
pair: Backend; PID pair: Backend; PID

View File

@ -83,6 +83,11 @@ The ``cursor`` class
The cursor will be unusable from this point forward; an The cursor will be unusable from this point forward; an
`~psycopg2.InterfaceError` will be raised if any operation is `~psycopg2.InterfaceError` will be raised if any operation is
attempted with the cursor. attempted with the cursor.
.. versionchanged:: 2.5 if the cursor is used in a ``with`` statement,
the method is automatically called at the end of the ``with``
block.
.. attribute:: closed .. attribute:: closed
@ -114,20 +119,51 @@ The ``cursor`` class
The `name` attribute is a Psycopg extension to the |DBAPI|. The `name` attribute is a Psycopg extension to the |DBAPI|.
.. attribute:: scrollable
Read/write attribute: specifies if a named cursor is declared
:sql:`SCROLL`, hence is capable to scroll backwards (using
`~cursor.scroll()`). If `!True`, the cursor can be scrolled backwards,
if `!False` it is never scrollable. If `!None` (default) the cursor
scroll option is not specified, usually but not always meaning no
backward scroll (see the |declare-notes|__).
.. |declare-notes| replace:: :sql:`DECLARE` notes
.. __: http://www.postgresql.org/docs/current/static/sql-declare.html#SQL-DECLARE-NOTES
.. note::
set the value before calling `~cursor.execute()` or use the
`connection.cursor()` *scrollable* parameter, otherwise the value
will have no effect.
.. versionadded:: 2.5
.. extension::
The `scrollable` attribute is a Psycopg extension to the |DBAPI|.
.. attribute:: withhold .. attribute:: withhold
Read/write attribute: specifies if a named cursor lifetime should Read/write attribute: specifies if a named cursor lifetime should
extend outside of the current transaction, i.e., it is possible to extend outside of the current transaction, i.e., it is possible to
fetch from the cursor even after a `commection.commit()` (but not after fetch from the cursor even after a `connection.commit()` (but not after
a `connection.rollback()`). See :ref:`server-side-cursors` a `connection.rollback()`). See :ref:`server-side-cursors`
.. note::
set the value before calling `~cursor.execute()` or use the
`connection.cursor()` *withhold* parameter, otherwise the value
will have no effect.
.. versionadded:: 2.4.3 .. versionadded:: 2.4.3
.. extension:: .. extension::
The `withhold` attribute is a Psycopg extension to the |DBAPI|. The `withhold` attribute is a Psycopg extension to the |DBAPI|.
.. |execute*| replace:: `execute*()` .. |execute*| replace:: `execute*()`
.. _execute*: .. _execute*:
@ -297,7 +333,8 @@ The ``cursor`` class
not changed. not changed.
The method can be used both for client-side cursors and The method can be used both for client-side cursors and
:ref:`server-side cursors <server-side-cursors>`. :ref:`server-side cursors <server-side-cursors>`. Server-side cursors
can usually scroll backwards only if declared `~cursor.scrollable`.
.. note:: .. note::
@ -527,10 +564,19 @@ The ``cursor`` class
|COPY|__ command documentation). |COPY|__ command documentation).
:param sql: the :sql:`COPY` statement to execute. :param sql: the :sql:`COPY` statement to execute.
:param file: a file-like object; must be a readable file for :param file: a file-like object to read or write (according to *sql*).
:sql:`COPY FROM` or an writable file for :sql:`COPY TO`.
:param size: size of the read buffer to be used in :sql:`COPY FROM`. :param size: size of the read buffer to be used in :sql:`COPY FROM`.
The *sql* statement should be in the form :samp:`COPY {table} TO
STDOUT` to export :samp:`{table}` to the *file* object passed as
argument or :samp:`COPY {table} FROM STDIN` to import the content of
the *file* object into :samp:`{table}`.
*file* must be a readable file-like object (as required by
`~cursor.copy_from()`) for *sql* statement :sql:`COPY ... FROM STDIN`
or a writable one (as required by `~cursor.copy_to()`) for :sql:`COPY
... TO STDOUT`.
Example: Example:
>>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout) >>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout)

View File

@ -50,7 +50,7 @@ An example of the available constants defined in the module:
'42P01' '42P01'
Constants representing all the error values documented by PostgreSQL versions Constants representing all the error values documented by PostgreSQL versions
between 8.1 and 9.1 are included in the module. between 8.1 and 9.2 are included in the module.
.. autofunction:: lookup(code) .. autofunction:: lookup(code)

View File

@ -13,7 +13,7 @@ The module contains a few objects and function extending the minimum set of
functionalities defined by the |DBAPI|_. functionalities defined by the |DBAPI|_.
.. class:: connection .. class:: connection(dsn, async=False)
Is the class usually returned by the `~psycopg2.connect()` function. Is the class usually returned by the `~psycopg2.connect()` function.
It is exposed by the `extensions` module in order to allow It is exposed by the `extensions` module in order to allow
@ -21,11 +21,9 @@ functionalities defined by the |DBAPI|_.
`!connect()` function using the `connection_factory` parameter. `!connect()` function using the `connection_factory` parameter.
See also :ref:`subclassing-connection`. See also :ref:`subclassing-connection`.
Subclasses should have constructor signature :samp:`({dsn}, {async}=0)`.
For a complete description of the class, see `connection`. For a complete description of the class, see `connection`.
.. class:: cursor .. class:: cursor(conn, name=None)
It is the class usually returnded by the `connection.cursor()` It is the class usually returnded by the `connection.cursor()`
method. It is exposed by the `extensions` module in order to allow method. It is exposed by the `extensions` module in order to allow
@ -139,6 +137,37 @@ functionalities defined by the |DBAPI|_.
.. automethod:: from_string(s) .. automethod:: from_string(s)
.. autoclass:: Diagnostics(exception)
.. versionadded:: 2.5
The attributes currently available are:
.. attribute::
column_name
constraint_name
context
datatype_name
internal_position
internal_query
message_detail
message_hint
message_primary
schema_name
severity
source_file
source_function
source_line
sqlstate
statement_position
table_name
A string with the error field if available; `!None` if not available.
The attribute value is available only if the error sent by the server:
not all the fields are available for all the errors and for all the
server versions.
.. autofunction:: set_wait_callback(f) .. autofunction:: set_wait_callback(f)
.. versionadded:: 2.2.0 .. versionadded:: 2.2.0

View File

@ -16,22 +16,27 @@ This module is a generic place used to hold little helper functions and
classes until a better place in the distribution is found. classes until a better place in the distribution is found.
.. index:: .. _cursor-subclasses:
pair: Cursor; Dictionary
.. _dict-cursor:
Connection and cursor subclasses Connection and cursor subclasses
-------------------------------- --------------------------------
A few objects that change the way the results are returned by the cursor or A few objects that change the way the results are returned by the cursor or
modify the object behavior in some other way. Typically `!connection` modify the object behavior in some other way. Typically `!cursor` subclasses
subclasses are passed as *connection_factory* argument to are passed as *cursor_factory* argument to `~psycopg2.connect()` so that the
`~psycopg2.connect()` so that the connection will generate the matching connection's `~connection.cursor()` method will generate objects of this
`!cursor` subclass. Alternatively a `!cursor` subclass can be used one-off by class. Alternatively a `!cursor` subclass can be used one-off by passing it
passing it as the *cursor_factory* argument to the `~connection.cursor()` as the *cursor_factory* argument to the `!cursor()` method.
method of a regular `!connection`.
If you want to use a `!connection` subclass you can pass it as the
*connection_factory* argument of the `!connect()` function.
.. index::
pair: Cursor; Dictionary
.. _dict-cursor:
Dictionary-like cursor Dictionary-like cursor
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
@ -61,6 +66,11 @@ The records still support indexing as the original tuple:
.. autoclass:: DictConnection .. autoclass:: DictConnection
.. note::
Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
``(dsn, cursor_factory=DictCursor)`` instead of `!DictConnection`.
.. autoclass:: DictRow .. autoclass:: DictRow
@ -71,6 +81,12 @@ Real dictionary cursor
.. autoclass:: RealDictConnection .. autoclass:: RealDictConnection
.. note::
Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
``(dsn, cursor_factory=RealDictCursor)`` instead of
`!RealDictConnection`.
.. autoclass:: RealDictRow .. autoclass:: RealDictRow
@ -101,6 +117,12 @@ expect it to be... ::
.. autoclass:: NamedTupleConnection .. autoclass:: NamedTupleConnection
.. note::
Not very useful since Psycopg 2.5: you can use `psycopg2.connect`\
``(dsn, cursor_factory=NamedTupleCursor)`` instead of
`!NamedTupleConnection`.
.. index:: .. index::
pair: Cursor; Logging pair: Cursor; Logging
@ -128,12 +150,100 @@ Additional data types
--------------------- ---------------------
.. _adapt-hstore: .. index::
pair: JSON; Data types
pair: JSON; Adaptation
.. _adapt-json:
JSON_ adaptation
^^^^^^^^^^^^^^^^
.. versionadded:: 2.5
Psycopg can adapt Python objects to and from the PostgreSQL |pgjson|_ type.
With PostgreSQL 9.2 adaptation is available out-of-the-box. To use JSON data
with previous database versions (either with the `9.1 json extension`__, but
even if you want to convert text fields to JSON) you can use
`register_json()`.
.. __: http://people.planetpostgresql.org/andrew/index.php?/archives/255-JSON-for-PG-9.2-...-and-now-for-9.1!.html
The Python library used to convert Python objects to JSON depends on the
language version: with Python 2.6 and following the :py:mod:`json` module from
the standard library is used; with previous versions the `simplejson`_ module
is used if available. Note that the last `!simplejson` version supporting
Python 2.4 is the 2.0.9.
.. _JSON: http://www.json.org/
.. |pgjson| replace:: :sql:`json`
.. _pgjson: http://www.postgresql.org/docs/current/static/datatype-json.html
.. _simplejson: http://pypi.python.org/pypi/simplejson/
In order to pass a Python object to the database as query argument you can use
the `Json` adapter::
curs.execute("insert into mytable (jsondata) values (%s)",
[Json({'a': 100})])
Reading from the database, |pgjson| values will be automatically converted to
Python objects.
.. note::
You can use `~psycopg2.extensions.register_adapter()` to adapt any Python
dictionary to JSON, either registering `Json` or any subclass or factory
creating a compatible adapter::
psycopg2.extensions.register_adapter(dict, psycopg2.extras.Json)
This setting is global though, so it is not compatible with similar
adapters such as the one registered by `register_hstore()`. Any other
object supported by JSON can be registered the same way, but this will
clobber the default adaptation rule, so be careful to unwanted side
effects.
If you want to customize the adaptation from Python to PostgreSQL you can
either provide a custom `!dumps()` function to `!Json`::
curs.execute("insert into mytable (jsondata) values (%s)",
[Json({'a': 100}, dumps=simplejson.dumps)])
or you can subclass it overriding the `~Json.dumps()` method::
class MyJson(Json):
def dumps(self, obj):
return simplejson.dumps(obj)
curs.execute("insert into mytable (jsondata) values (%s)",
[MyJson({'a': 100})])
Customizing the conversion from PostgreSQL to Python can be done passing a
custom `!loads()` function to `register_json()` (or `register_default_json()`
for PostgreSQL 9.2). For example, if you want to convert the float values
from :sql:`json` into :py:class:`~decimal.Decimal` you can use::
loads = lambda x: json.loads(x, parse_float=Decimal)
psycopg2.extras.register_json(conn, loads=loads)
.. autoclass:: Json
.. automethod:: dumps
.. autofunction:: register_json
.. autofunction:: register_default_json
.. index:: .. index::
pair: hstore; Data types pair: hstore; Data types
pair: dict; Adaptation pair: dict; Adaptation
.. _adapt-hstore:
Hstore data type Hstore data type
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
@ -141,7 +251,7 @@ Hstore data type
The |hstore|_ data type is a key-value store embedded in PostgreSQL. It has The |hstore|_ data type is a key-value store embedded in PostgreSQL. It has
been available for several server versions but with the release 9.0 it has been available for several server versions but with the release 9.0 it has
been greatly improved in capacity and usefulness with the addiction of many been greatly improved in capacity and usefulness with the addition of many
functions. It supports GiST or GIN indexes allowing search by keys or functions. It supports GiST or GIN indexes allowing search by keys or
key/value pairs as well as regular BTree indexes for equality, uniqueness etc. key/value pairs as well as regular BTree indexes for equality, uniqueness etc.
@ -168,13 +278,13 @@ can be enabled using the `register_hstore()` function.
.. _adapt-composite:
.. index:: .. index::
pair: Composite types; Data types pair: Composite types; Data types
pair: tuple; Adaptation pair: tuple; Adaptation
pair: namedtuple; Adaptation pair: namedtuple; Adaptation
.. _adapt-composite:
Composite types casting Composite types casting
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
@ -198,8 +308,8 @@ after a table row type) into a Python named tuple, or into a regular tuple if
>>> cur.fetchone()[0] >>> cur.fetchone()[0]
card(value=8, suit='hearts') card(value=8, suit='hearts')
Nested composite types are handled as expected, but the type of the composite Nested composite types are handled as expected, provided that the type of the
components must be registered as well. composite components are registered as well.
.. doctest:: .. doctest::
@ -214,15 +324,167 @@ components must be registered as well.
Adaptation from Python tuples to composite types is automatic instead and Adaptation from Python tuples to composite types is automatic instead and
requires no adapter registration. requires no adapter registration.
.. _custom-composite:
.. Note::
If you want to convert PostgreSQL composite types into something different
than a `!namedtuple` you can subclass the `CompositeCaster` overriding
`~CompositeCaster.make()`. For example, if you want to convert your type
into a Python dictionary you can use::
>>> class DictComposite(psycopg2.extras.CompositeCaster):
... def make(self, values):
... return dict(zip(self.attnames, values))
>>> psycopg2.extras.register_composite('card', cur,
... factory=DictComposite)
>>> cur.execute("select (8, 'hearts')::card")
>>> cur.fetchone()[0]
{'suit': 'hearts', 'value': 8}
.. autofunction:: register_composite .. autofunction:: register_composite
.. versionchanged:: 2.4.3
added support for array of composite types
.. versionchanged:: 2.5
added the *factory* parameter
.. autoclass:: CompositeCaster .. autoclass:: CompositeCaster
.. automethod:: make
.. versionadded:: 2.5
Object attributes:
.. attribute:: name
The name of the PostgreSQL type.
.. attribute:: schema
The schema where the type is defined.
.. versionadded:: 2.5
.. attribute:: oid
The oid of the PostgreSQL type.
.. attribute:: array_oid
The oid of the PostgreSQL array type, if available.
.. attribute:: type
The type of the Python objects returned. If :py:func:`collections.namedtuple()`
is available, it is a named tuple with attributes equal to the type
components. Otherwise it is just the `!tuple` object.
.. attribute:: attnames
List of component names of the type to be casted.
.. attribute:: atttypes
List of component type oids of the type to be casted.
.. index::
pair: range; Data types
.. _adapt-range:
Range data types
^^^^^^^^^^^^^^^^
.. versionadded:: 2.5
Psycopg offers a `Range` Python type and supports adaptation between them and
PostgreSQL |range|_ types. Builtin |range| types are supported out-of-the-box;
user-defined |range| types can be adapted using `register_range()`.
.. |range| replace:: :sql:`range`
.. _range: http://www.postgresql.org/docs/current/static/rangetypes.html
.. autoclass:: Range
This Python type is only used to pass and retrieve range values to and
from PostgreSQL and doesn't attempt to replicate the PostgreSQL range
features: it doesn't perform normalization and doesn't implement all the
operators__ supported by the database.
.. __: http://www.postgresql.org/docs/current/static/functions-range.html#RANGE-OPERATORS-TABLE
`!Range` objects are immutable, hashable, and support the ``in`` operator
(checking if an element is within the range). They can be tested for
equivalence but not for ordering. Empty ranges evaluate to `!False` in
boolean context, nonempty evaluate to `!True`.
Although it is possible to instantiate `!Range` objects, the class doesn't
have an adapter registered, so you cannot normally pass these instances as
query arguments. To use range objects as query arguments you can either
use one of the provided subclasses, such as `NumericRange` or create a
custom subclass using `register_range()`.
Object attributes:
.. autoattribute:: isempty
.. autoattribute:: lower
.. autoattribute:: upper
.. autoattribute:: lower_inc
.. autoattribute:: upper_inc
.. autoattribute:: lower_inf
.. autoattribute:: upper_inf
The following `Range` subclasses map builtin PostgreSQL |range| types to
Python objects: they have an adapter registered so their instances can be
passed as query arguments. |range| values read from database queries are
automatically casted into instances of these classes.
.. autoclass:: NumericRange
.. autoclass:: DateRange
.. autoclass:: DateTimeRange
.. autoclass:: DateTimeTZRange
Custom |range| types (created with |CREATE TYPE|_ :sql:`... AS RANGE`) can be
adapted to a custom `Range` subclass:
.. autofunction:: register_range
.. autoclass:: RangeCaster
Object attributes:
.. attribute:: range
The `!Range` subclass adapted.
.. attribute:: adapter
The `~psycopg2.extensions.ISQLQuote` responsible to adapt `!range`.
.. attribute:: typecaster
The object responsible for casting.
.. attribute:: array_typecaster
The object responsible to cast arrays, if available, else `!None`.
.. index:: .. index::
pair: UUID; Data types pair: UUID; Data types
.. _adapt-uuid:
UUID data type UUID data type
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^

View File

@ -7,6 +7,8 @@ Here are a few gotchas you may encounter using `psycopg2`. Feel free to
suggest new entries! suggest new entries!
.. _faq-transactions:
Problems with transactions handling Problems with transactions handling
----------------------------------- -----------------------------------
@ -51,6 +53,8 @@ Why do I get the error *current transaction is aborted, commands ignored until e
informations. informations.
.. _faq-types:
Problems with type conversions Problems with type conversions
------------------------------ ------------------------------
@ -151,6 +155,8 @@ Arrays of *TYPE* are not casted to list.
provided in the `~psycopg2.extensions.new_array_type()` documentation. provided in the `~psycopg2.extensions.new_array_type()` documentation.
.. _faq-best-practices:
Best practices Best practices
-------------- --------------
@ -191,6 +197,8 @@ What are the advantages or disadvantages of using named cursors?
little memory on the client and to skip or discard parts of the result set. little memory on the client and to skip or discard parts of the result set.
.. _faq-compile:
Problems compiling and deploying psycopg2 Problems compiling and deploying psycopg2
----------------------------------------- -----------------------------------------

View File

@ -4,12 +4,12 @@ Psycopg -- PostgreSQL database adapter for Python
.. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com> .. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com>
Psycopg_ is a PostgreSQL_ database adapter for the Python_ programming Psycopg_ is the most popular PostgreSQL_ database adapter for the Python_
language. Its main features are that it supports the full Python |DBAPI|_ programming language. Its main features are the complete implementation of
and it is thread safe (threads can share the connections). It was designed for the Python |DBAPI|_ specification and the thread safety (several threads can
heavily multi-threaded applications that create and destroy lots of cursors and share the same connection). It was designed for heavily multi-threaded
make a large number of concurrent :sql:`INSERT`\ s or :sql:`UPDATE`\ s. applications that create and destroy lots of cursors and make a large number
The Psycopg distribution includes ZPsycopgDA, a Zope_ Database Adapter. of concurrent :sql:`INSERT`\s or :sql:`UPDATE`\s.
Psycopg 2 is mostly implemented in C as a libpq_ wrapper, resulting in being Psycopg 2 is mostly implemented in C as a libpq_ wrapper, resulting in being
both efficient and secure. It features client-side and :ref:`server-side both efficient and secure. It features client-side and :ref:`server-side
@ -18,12 +18,13 @@ both efficient and secure. It features client-side and :ref:`server-side
support, and a flexible :ref:`objects adaptation system support, and a flexible :ref:`objects adaptation system
<python-types-adaptation>`. Many basic Python types are supported <python-types-adaptation>`. Many basic Python types are supported
out-of-the-box and mapped to matching PostgreSQL data types, such as strings out-of-the-box and mapped to matching PostgreSQL data types, such as strings
(both bytes and Unicode), numbers (ints, longs, floats, decimals), booleans and (both byte strings and Unicode), numbers (ints, longs, floats, decimals),
datetime objects (both built-in and `mx.DateTime`_), several types of booleans and date/time objects (both built-in and `mx.DateTime`_), several
:ref:`binary objects <adapt-binary>`. Also available are mappings between lists types of :ref:`binary objects <adapt-binary>`. Also available are mappings
and PostgreSQL arrays of any supported type, between :ref:`dictionaries and between lists and PostgreSQL arrays of any supported type, between
PostgreSQL hstores <adapt-hstore>`, and between :ref:`tuples/namedtuples and :ref:`dictionaries and PostgreSQL hstore <adapt-hstore>`, between
PostgreSQL composite types <adapt-composite>`. :ref:`tuples/namedtuples and PostgreSQL composite types <adapt-composite>`,
and between Python objects and :ref:`JSON <adapt-json>`.
Psycopg 2 is both Unicode and Python 3 friendly. Psycopg 2 is both Unicode and Python 3 friendly.
@ -31,7 +32,6 @@ Psycopg 2 is both Unicode and Python 3 friendly.
.. _Psycopg: http://initd.org/psycopg/ .. _Psycopg: http://initd.org/psycopg/
.. _PostgreSQL: http://www.postgresql.org/ .. _PostgreSQL: http://www.postgresql.org/
.. _Python: http://www.python.org/ .. _Python: http://www.python.org/
.. _Zope: http://www.zope.org/
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html .. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
.. |COPY-TO-FROM| replace:: :sql:`COPY TO/COPY FROM` .. |COPY-TO-FROM| replace:: :sql:`COPY TO/COPY FROM`
.. __: http://www.postgresql.org/docs/current/static/sql-copy.html .. __: http://www.postgresql.org/docs/current/static/sql-copy.html
@ -42,6 +42,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
install
usage usage
module module
connection connection
@ -53,6 +54,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
extras extras
errorcodes errorcodes
faq faq
news
.. ifconfig:: builder != 'text' .. ifconfig:: builder != 'text'

258
doc/src/install.rst Normal file
View File

@ -0,0 +1,258 @@
Introduction
============
.. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com>
Psycopg is a PostgreSQL_ adapter for the Python_ programming language. It is a
wrapper for the libpq_, the official PostgreSQL client library.
The `psycopg2` package is the current mature implementation of the adapter: it
is a C extension and as such it is only compatible with CPython_. If you want
to use Psycopg on a different Python implementation (PyPy, Jython, IronPython)
there is an experimental `porting of Psycopg for Ctypes`__, but it is not as
mature as the C implementation yet.
The current `!psycopg2` implementation supports:
- Python 2 versions from 2.5 to 2.7
- Python 3 versions from 3.1 to 3.3
- PostgreSQL versions from 7.4 to 9.2
.. _PostgreSQL: http://www.postgresql.org/
.. _Python: http://www.python.org/
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
.. _CPython: http://en.wikipedia.org/wiki/CPython
.. _Ctypes: http://docs.python.org/library/ctypes.html
.. __: https://github.com/mvantellingen/psycopg2-ctypes
.. note::
`!psycopg2` usually depends at runtime on the libpq dynamic library.
However it can connect to PostgreSQL servers of any supported version,
independently of the version of the libpq used: just install the most
recent libpq version or the most practical, without trying to match it to
the version of the PostgreSQL server you will have to connect to.
Installation
============
If possible, and usually it is, please :ref:`install Psycopg from a package
<install-from-package>` available for your distribution or operating system.
Compiling from source is a very easy task, however `!psycopg2` is a C
extension module and as such it requires a few more things in place respect to
a pure Python module. So, if you don't have experience compiling Python
extension packages, *above all if you are a Windows or a Mac OS user*, please
use a pre-compiled package and go straight to the :ref:`module usage <usage>`
avoid bothering with the gory details.
.. _install-from-package:
Install from a package
----------------------
.. index::
pair: Install; Linux
**Linux**
Psycopg is available already packaged in many Linux distributions: look
for a package such as ``python-psycopg2`` using the package manager of
your choice.
On Debian, Ubuntu and other deb-based distributions you should just need::
sudo apt-get install python-psycopg2
to install the package with all its dependencies.
.. index::
pair: Install; Mac OS X
**Mac OS X**
Psycopg is available as a `fink package`__ in the *unstable* tree: you may
install it with::
fink install psycopg2-py27
.. __: http://pdb.finkproject.org/pdb/package.php/psycopg2-py27
The library is also available on `MacPorts`__ try::
sudo port install py27-psycopg2
.. __: http://www.macports.org/
.. index::
pair: Install; Windows
**Microsoft Windows**
Jason Erickson maintains a packaged `Windows port of Psycopg`__ with
installation executable. Download. Double click. Done.
.. __: http://www.stickpeople.com/projects/python/win-psycopg/
.. index::
single: Install; from source
.. _install-from-source:
Install from source
-------------------
These notes illustrate how to compile Psycopg on Linux. If you want to compile
Psycopg on other platforms you may have to adjust some details accordingly.
.. _requirements:
Psycopg is a C wrapper to the libpq PostgreSQL client library. To install it
from sources you will need:
- A C compiler.
- The Python header files. They are usually installed in a package such as
**python-dev**. A message such as *error: Python.h: No such file or
directory* is an indication that the Python headers are missing.
- The libpq header files. They are usually installed in a package such as
**libpq-dev**. If you get an *error: libpq-fe.h: No such file or directory*
you are missing them.
- The :program:`pg_config` program: it is usually installed by the
**libpq-dev** package but sometimes it is not in a :envvar:`PATH` directory.
Having it in the :envvar:`PATH` greatly streamlines the installation, so try
running ``pg_config --version``: if it returns an error or an unexpected
version number then locate the directory containing the :program:`pg_config`
shipped with the right libpq version (usually
``/usr/lib/postgresql/X.Y/bin/``) and add it to the :envvar:`PATH`::
$ export PATH=/usr/lib/postgresql/X.Y/bin/:$PATH
You only need it to compile and install `!psycopg2`, not for its regular
usage.
.. note::
The libpq header files used to compile `!psycopg2` should match the
version of the library linked at runtime. If you get errors about missing
or mismatching libraries when importing `!psycopg2` check (e.g. using
:program:`ldd`) if the module ``psycopg2/_psycopg.so`` is linked to the
right ``libpq.so``.
.. index::
single: Install; from PyPI
.. _package-manager:
Use a Python package manager
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If the above requirements are satisfied, you can use :program:`easy_install`,
:program:`pip` or whatever the Python package manager of the week::
$ pip install psycopg2
Please refer to your package manager documentation about performing a local or
global installation, :program:`virtualenv` (fully supported by recent Psycopg
versions), using different Python versions and other nuances.
.. index::
single: setup.py
single: setup.cfg
.. _source-package:
Use the source package
^^^^^^^^^^^^^^^^^^^^^^
You can download a copy of Psycopg source files from the `Psycopg download
page`__. Once unpackaged, to compile and install the package you can run::
$ python setup.py build
$ sudo python setup.py install
If you have less standard requirements such as:
- creating a :ref:`debug build <debug-build>`,
- using :program:`pg_config` not in the :envvar:`PATH`,
- supporting ``mx.DateTime``,
then take a look at the ``setup.cfg`` file.
Some of the options available in ``setup.cfg`` are also available as command
line arguments of the ``build_ext`` sub-command. For instance you can specify
an alternate :program:`pg_config` version using::
$ python setup.py build_ext --pg-config /path/to/pg_config build
Use ``python setup.py build_ext --help`` to get a list of the options
supported.
.. __: http://initd.org/psycopg/download/
.. index::
single: debug
single: PSYCOPG_DEBUG
.. _debug-build:
Creating a debug build
----------------------
In case of problems, Psycopg can be configured to emit detailed debug
messages, which can be very useful for diagnostics and to report a bug. In
order to create a debug package:
- `Download`__ and unpack the Psycopg source package.
- Edit the ``setup.cfg`` file adding the ``PSYCOPG_DEBUG`` flag to the
``define`` option.
- :ref:`Compile and install <source-package>` the package.
- Set the :envvar:`PSYCOPG_DEBUG` variable::
$ export PSYCOPG_DEBUG=1
- Run your program (making sure that the `!psycopg2` package imported is the
one you just compiled and not e.g. the system one): you will have a copious
stream of informations printed on stdout.
.. __: http://initd.org/psycopg/download/
.. _other-problems:
If you still have problems
--------------------------
Try the following. *In order:*
- Read again the :ref:`requirements <requirements>`.
- Read the :ref:`FAQ <faq-compile>`.
- Google for `!psycopg2` *your error message*. Especially useful the week
after the release of a new OS X version.
- Write to the `Mailing List`__.
- Complain on your blog or on Twitter that `!psycopg2` is the worst package
ever and about the quality time you have wasted figuring out the correct
:envvar:`ARCHFLAGS`. Especially useful from the Starbucks near you.
.. __: http://mail.postgresql.org/mj/mj_wwwusr/domain=postgresql.org?func=lists-long-full&extra=psycopg

View File

@ -7,7 +7,7 @@ The `psycopg2` module content
The module interface respects the standard defined in the |DBAPI|_. The module interface respects the standard defined in the |DBAPI|_.
.. index:: .. index::
single: Connection string single: Connection string
double: Connection; Parameters double: Connection; Parameters
single: Username; Connection single: Username; Connection
@ -16,11 +16,14 @@ The module interface respects the standard defined in the |DBAPI|_.
single: Port; Connection single: Port; Connection
single: DSN (Database Source Name) single: DSN (Database Source Name)
.. function:: connect(dsn or params [, connection_factory] [, async=0]) .. function::
connect(dsn, connection_factory=None, cursor_factory=None, async=False)
connect(\*\*kwargs, connection_factory=None, cursor_factory=None, async=False)
Create a new database session and return a new `connection` object. Create a new database session and return a new `connection` object.
The connection parameters can be specified either as a string:: The connection parameters can be specified either as a `libpq connection
string`__ using the *dsn* parameter::
conn = psycopg2.connect("dbname=test user=postgres password=secret") conn = psycopg2.connect("dbname=test user=postgres password=secret")
@ -28,9 +31,15 @@ The module interface respects the standard defined in the |DBAPI|_.
conn = psycopg2.connect(database="test", user="postgres", password="secret") conn = psycopg2.connect(database="test", user="postgres", password="secret")
The two call styles are mutually exclusive: you cannot specify connection
parameters as keyword arguments together with a connection string; only
the parameters not needed for the database connection (*i.e.*
*connection_factory*, *cursor_factory*, and *async*) are supported
together with the *dsn* argument.
The basic connection parameters are: The basic connection parameters are:
- `!dbname` -- the database name (only in dsn string) - `!dbname` -- the database name (only in the *dsn* string)
- `!database` -- the database name (only as keyword argument) - `!database` -- the database name (only as keyword argument)
- `!user` -- user name used to authenticate - `!user` -- user name used to authenticate
- `!password` -- password used to authenticate - `!password` -- password used to authenticate
@ -38,26 +47,45 @@ The module interface respects the standard defined in the |DBAPI|_.
- `!port` -- connection port number (defaults to 5432 if not provided) - `!port` -- connection port number (defaults to 5432 if not provided)
Any other connection parameter supported by the client library/server can Any other connection parameter supported by the client library/server can
be passed either in the connection string or as keyword. See the be passed either in the connection string or as keywords. The PostgreSQL
PostgreSQL documentation for a complete `list of supported parameters`__. documentation contains the complete list of the `supported parameters`__.
Also note that the same parameters can be passed to the client library Also note that the same parameters can be passed to the client library
using `environment variables`__. using `environment variables`__.
.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS .. __:
.. __: http://www.postgresql.org/docs/current/static/libpq-envars.html .. _connstring: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
.. __:
.. _connparams: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS
.. __:
.. _connenvvars: http://www.postgresql.org/docs/current/static/libpq-envars.html
Using the *connection_factory* parameter a different class or Using the *connection_factory* parameter a different class or
connections factory can be specified. It should be a callable object connections factory can be specified. It should be a callable object
taking a *dsn* argument. See :ref:`subclassing-connection` for taking a *dsn* string argument. See :ref:`subclassing-connection` for
details. details. If a *cursor_factory* is specified, the connection's
`~connection.cursor_factory` is set to it. If you only need customized
cursors you can use this parameter instead of subclassing a connection.
Using *async*\=1 an asynchronous connection will be created: see Using *async*\=\ `!True` an asynchronous connection will be created: see
:ref:`async-support` to know about advantages and limitations. :ref:`async-support` to know about advantages and limitations.
.. versionchanged:: 2.4.3 .. versionchanged:: 2.4.3
any keyword argument is passed to the connection. Previously only the any keyword argument is passed to the connection. Previously only the
basic parameters (plus `!sslmode`) were supported as keywords. basic parameters (plus `!sslmode`) were supported as keywords.
.. versionchanged:: 2.5
added the *cursor_factory* parameter.
.. seealso::
- libpq `connection string syntax`__
- libpq supported `connection parameters`__
- libpq supported `environment variables`__
.. __: connstring_
.. __: connparams_
.. __: connenvvars_
.. extension:: .. extension::
The parameters *connection_factory* and *async* are Psycopg extensions The parameters *connection_factory* and *async* are Psycopg extensions
@ -135,10 +163,27 @@ available through the following exceptions:
The cursor the exception was raised from; `None` if not applicable. The cursor the exception was raised from; `None` if not applicable.
.. attribute:: diag
A `~psycopg2.extensions.Diagnostics` object containing further
information about the error. ::
>>> try:
... cur.execute("SELECT * FROM barf")
... except Exception, e:
... pass
>>> e.diag.severity
'ERROR'
>>> e.diag.message_primary
'relation "barf" does not exist'
.. versionadded:: 2.5
.. extension:: .. extension::
The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes The `~Error.pgerror`, `~Error.pgcode`, `~Error.cursor`, and
are Psycopg extensions. `~Error.diag` attributes are Psycopg extensions.
.. exception:: InterfaceError .. exception:: InterfaceError
@ -258,15 +303,15 @@ the type codes for date, time and timestamp columns; see the
Implementation Hints below for details). Implementation Hints below for details).
The module exports the following constructors and singletons: The module exports the following constructors and singletons:
.. function:: Date(year,month,day) .. function:: Date(year,month,day)
This function constructs an object holding a date value. This function constructs an object holding a date value.
.. function:: Time(hour,minute,second) .. function:: Time(hour,minute,second)
This function constructs an object holding a time value. This function constructs an object holding a time value.
.. function:: Timestamp(year,month,day,hour,minute,second) .. function:: Timestamp(year,month,day,hour,minute,second)
This function constructs an object holding a time stamp value. This function constructs an object holding a time stamp value.
@ -278,11 +323,11 @@ The module exports the following constructors and singletons:
the standard Python time module for details). the standard Python time module for details).
.. function:: TimeFromTicks(ticks) .. function:: TimeFromTicks(ticks)
This function constructs an object holding a time value from the given This function constructs an object holding a time value from the given
ticks value (number of seconds since the epoch; see the documentation of ticks value (number of seconds since the epoch; see the documentation of
the standard Python time module for details). the standard Python time module for details).
.. function:: TimestampFromTicks(ticks) .. function:: TimestampFromTicks(ticks)
This function constructs an object holding a time stamp value from the This function constructs an object holding a time stamp value from the
@ -290,10 +335,16 @@ The module exports the following constructors and singletons:
documentation of the standard Python time module for details). documentation of the standard Python time module for details).
.. function:: Binary(string) .. function:: Binary(string)
This function constructs an object capable of holding a binary (long) This function constructs an object capable of holding a binary (long)
string value. string value.
.. note::
All the adapters returned by the module level factories (`!Binary`,
`!Date`, `!Time`, `!Timestamp` and the `!*FromTicks` variants) expose the
wrapped object (a regular Python object such as `!datetime`) in an
`!adapted` attribute.
.. data:: STRING .. data:: STRING
@ -304,17 +355,17 @@ The module exports the following constructors and singletons:
This type object is used to describe (long) binary columns in a database This type object is used to describe (long) binary columns in a database
(e.g. LONG, RAW, BLOBs). (e.g. LONG, RAW, BLOBs).
.. data:: NUMBER .. data:: NUMBER
This type object is used to describe numeric columns in a database. This type object is used to describe numeric columns in a database.
.. data:: DATETIME .. data:: DATETIME
This type object is used to describe date/time columns in a database. This type object is used to describe date/time columns in a database.
.. data:: ROWID .. data:: ROWID
This type object is used to describe the "Row ID" column in a database. This type object is used to describe the "Row ID" column in a database.

4
doc/src/news.rst Normal file
View File

@ -0,0 +1,4 @@
Release notes
=============
.. include:: ../../NEWS

View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
"""
ticket role
~~~~~~~~~~~
An interpreted text role to link docs to lighthouse issues.
:copyright: Copyright 2013 by Daniele Varrazzo.
"""
from docutils import nodes, utils
from docutils.parsers.rst import roles
def ticket_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
try:
num = int(text.replace('#', ''))
except ValueError:
msg = inliner.reporter.error(
"ticket number must be... a number, got '%s'" % text)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
url_pattern = inliner.document.settings.env.app.config.ticket_url
if url_pattern is None:
msg = inliner.reporter.warning(
"ticket not configured: please configure ticket_url in conf.py")
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
url = url_pattern % num
roles.set_classes(options)
node = nodes.reference(rawtext, 'ticket ' + utils.unescape(text),
refuri=url, **options)
return [node], []
def setup(app):
app.add_config_value('ticket_url', None, 'env')
app.add_role('ticket', ticket_role)

View File

@ -1,3 +1,5 @@
.. _usage:
Basic module usage Basic module usage
================== ==================
@ -49,7 +51,7 @@ The main entry points of Psycopg are:
- create new `cursor`\s using the `~connection.cursor()` method to - create new `cursor`\s using the `~connection.cursor()` method to
execute database commands and queries, execute database commands and queries,
- terminate the session using the methods `~connection.commit()` or - terminate transactions using the methods `~connection.commit()` or
`~connection.rollback()`. `~connection.rollback()`.
- The class `cursor` allows interaction with the database: - The class `cursor` allows interaction with the database:
@ -202,28 +204,88 @@ Adaptation of Python values to SQL types
Many standard Python types are adapted into SQL and returned as Python Many standard Python types are adapted into SQL and returned as Python
objects when a query is executed. objects when a query is executed.
If you need to convert other Python types to and from PostgreSQL data types, The following table shows the default mapping between Python and PostgreSQL
see :ref:`adapting-new-types` and :ref:`type-casting-from-sql-to-python`. You types:
can also find a few other specialized adapters in the `psycopg2.extras`
module.
In the following examples the method `~cursor.mogrify()` is used to show ..
the SQL string that would be sent to the database. TODO: The table is not rendered in text output
.. only:: html
.. table::
:class: data-types
+--------------------+-------------------------+--------------------------+
| Python | PostgreSQL | See also |
+====================+=========================+==========================+
| `!None` | :sql:`NULL` | :ref:`adapt-consts` |
+--------------------+-------------------------+ |
| `!bool` | :sql:`bool` | |
+--------------------+-------------------------+--------------------------+
| `!float` | | :sql:`real` | :ref:`adapt-numbers` |
| | | :sql:`double` | |
+--------------------+-------------------------+ |
| | `!int` | | :sql:`smallint` | |
| | `!long` | | :sql:`integer` | |
| | | :sql:`bigint` | |
+--------------------+-------------------------+ |
| `~decimal.Decimal` | :sql:`numeric` | |
+--------------------+-------------------------+--------------------------+
| | `!str` | | :sql:`varchar` | :ref:`adapt-string` |
| | `!unicode` | | :sql:`text` | |
+--------------------+-------------------------+--------------------------+
| | `buffer` | :sql:`bytea` | :ref:`adapt-binary` |
| | `memoryview` | | |
| | `bytearray` | | |
| | `bytes` | | |
| | Buffer protocol | | |
+--------------------+-------------------------+--------------------------+
| `!date` | :sql:`date` | :ref:`adapt-date` |
+--------------------+-------------------------+ |
| `!time` | :sql:`time` | |
+--------------------+-------------------------+ |
| `!datetime` | | :sql:`timestamp` | |
| | | :sql:`timestamptz` | |
+--------------------+-------------------------+ |
| `!timedelta` | :sql:`interval` | |
+--------------------+-------------------------+--------------------------+
| `!list` | :sql:`ARRAY` | :ref:`adapt-list` |
+--------------------+-------------------------+--------------------------+
| | `!tuple` | | Composite types | | :ref:`adapt-tuple` |
| | `!namedtuple` | | :sql:`IN` syntax | | :ref:`adapt-composite` |
+--------------------+-------------------------+--------------------------+
| `!dict` | :sql:`hstore` | :ref:`adapt-hstore` |
+--------------------+-------------------------+--------------------------+
| Psycopg's `!Range` | :sql:`range` | :ref:`adapt-range` |
+--------------------+-------------------------+--------------------------+
| Anything\ |tm| | :sql:`json` | :ref:`adapt-json` |
+--------------------+-------------------------+--------------------------+
| `uuid` | :sql:`uuid` | :ref:`adapt-uuid` |
+--------------------+-------------------------+--------------------------+
.. |tm| unicode:: U+2122
The mapping is fairly customizable: see :ref:`adapting-new-types` and
:ref:`type-casting-from-sql-to-python`. You can also find a few other
specialized adapters in the `psycopg2.extras` module.
.. _adapt-consts:
.. index:: .. index::
pair: None; Adaptation pair: None; Adaptation
single: NULL; Adaptation single: NULL; Adaptation
pair: Boolean; Adaptation pair: Boolean; Adaptation
- Python `None` and boolean values `True` and `False` are converted into the .. _adapt-consts:
proper SQL literals::
Constants adaptation
^^^^^^^^^^^^^^^^^^^^
Python `None` and boolean values `True` and `False` are converted into the
proper SQL literals::
>>> cur.mogrify("SELECT %s, %s, %s;", (None, True, False)) >>> cur.mogrify("SELECT %s, %s, %s;", (None, True, False))
>>> 'SELECT NULL, true, false;' 'SELECT NULL, true, false;'
.. _adapt-numbers:
.. index:: .. index::
single: Adaptation; numbers single: Adaptation; numbers
@ -231,168 +293,48 @@ the SQL string that would be sent to the database.
single: Float; Adaptation single: Float; Adaptation
single: Decimal; Adaptation single: Decimal; Adaptation
- Numeric objects: `int`, `long`, `float`, `~decimal.Decimal` are converted in .. _adapt-numbers:
the PostgreSQL numerical representation::
Numbers adaptation
^^^^^^^^^^^^^^^^^^
Numeric objects: `int`, `long`, `float`, `~decimal.Decimal` are converted in
the PostgreSQL numerical representation::
>>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00"))) >>> cur.mogrify("SELECT %s, %s, %s, %s;", (10, 10L, 10.0, Decimal("10.00")))
>>> 'SELECT 10, 10, 10.0, 10.00;' 'SELECT 10, 10, 10.0, 10.00;'
Reading from the database, integer types are converted into `!int`, floating
point types are converted into `!float`, :sql:`numeric`\/\ :sql:`decimal` are
converted into `!Decimal`.
.. note::
Sometimes you may prefer to receive :sql:`numeric` data as `!float`
insted, for performance reason or ease of manipulation: you can configure
an adapter to :ref:`cast PostgreSQL numeric to Python float <faq-float>`.
This of course may imply a loss of precision.
.. seealso:: `PostgreSQL numeric types
<http://www.postgresql.org/docs/current/static/datatype-numeric.html>`__
.. _adapt-string:
.. index:: .. index::
pair: Strings; Adaptation pair: Strings; Adaptation
single: Unicode; Adaptation single: Unicode; Adaptation
- String types: `str`, `unicode` are converted in SQL string syntax. .. _adapt-string:
`!unicode` objects (`!str` in Python 3) are encoded in the connection
`~connection.encoding` to be sent to the backend: trying to send a character
not supported by the encoding will result in an error. Received data can be
converted either as `!str` or `!unicode`: see :ref:`unicode-handling`.
.. _adapt-binary: Strings adaptation
^^^^^^^^^^^^^^^^^^
.. index:: Python `str` and `unicode` are converted into the SQL string syntax.
single: Buffer; Adaptation `!unicode` objects (`!str` in Python 3) are encoded in the connection
single: bytea; Adaptation `~connection.encoding` before sending to the backend: trying to send a
single: bytes; Adaptation character not supported by the encoding will result in an error. Data is
single: bytearray; Adaptation usually received as `!str` (*i.e.* it is *decoded* on Python 3, left *encoded*
single: memoryview; Adaptation on Python 2). However it is possible to receive `!unicode` on Python 2 too:
single: Binary string see :ref:`unicode-handling`.
- Binary types: Python types representing binary objects are converted into
PostgreSQL binary string syntax, suitable for :sql:`bytea` fields. Such
types are `buffer` (only available in Python 2), `memoryview` (available
from Python 2.7), `bytearray` (available from Python 2.6) and `bytes`
(only from Python 3: the name is available from Python 2.6 but it's only an
alias for the type `!str`). Any object implementing the `Revised Buffer
Protocol`__ should be usable as binary type where the protocol is supported
(i.e. from Python 2.6). Received data is returned as `!buffer` (in Python 2)
or `!memoryview` (in Python 3).
.. __: http://www.python.org/dev/peps/pep-3118/
.. versionchanged:: 2.4
only strings were supported before.
.. versionchanged:: 2.4.1
can parse the 'hex' format from 9.0 servers without relying on the
version of the client library.
.. note::
In Python 2, if you have binary data in a `!str` object, you can pass them
to a :sql:`bytea` field using the `psycopg2.Binary` wrapper::
mypic = open('picture.png', 'rb').read()
curs.execute("insert into blobs (file) values (%s)",
(psycopg2.Binary(mypic),))
.. warning::
Since version 9.0 PostgreSQL uses by default `a new "hex" format`__ to
emit :sql:`bytea` fields. Starting from Psycopg 2.4.1 the format is
correctly supported. If you use a previous version you will need some
extra care when receiving bytea from PostgreSQL: you must have at least
libpq 9.0 installed on the client or alternatively you can set the
`bytea_output`__ configuration parameter to ``escape``, either in the
server configuration file or in the client session (using a query such as
``SET bytea_output TO escape;``) before receiving binary data.
.. __: http://www.postgresql.org/docs/current/static/datatype-binary.html
.. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. _adapt-date:
.. index::
single: Adaptation; Date/Time objects
single: Date objects; Adaptation
single: Time objects; Adaptation
single: Interval objects; Adaptation
single: mx.DateTime; Adaptation
- Date and time objects: builtin `~datetime.datetime`, `~datetime.date`,
`~datetime.time`, `~datetime.timedelta` are converted into PostgreSQL's
:sql:`timestamp`, :sql:`date`, :sql:`time`, :sql:`interval` data types.
Time zones are supported too. The Egenix `mx.DateTime`_ objects are adapted
the same way::
>>> dt = datetime.datetime.now()
>>> dt
datetime.datetime(2010, 2, 8, 1, 40, 27, 425337)
>>> cur.mogrify("SELECT %s, %s, %s;", (dt, dt.date(), dt.time()))
"SELECT '2010-02-08T01:40:27.425337', '2010-02-08', '01:40:27.425337';"
>>> cur.mogrify("SELECT %s;", (dt - datetime.datetime(2010,1,1),))
"SELECT '38 days 6027.425337 seconds';"
.. _adapt-list:
.. index::
single: Array; Adaptation
double: Lists; Adaptation
- Python lists are converted into PostgreSQL :sql:`ARRAY`\ s::
>>> cur.mogrify("SELECT %s;", ([10, 20, 30], ))
'SELECT ARRAY[10, 20, 30];'
.. note::
Reading back from PostgreSQL, arrays are converted to list of Python
objects as expected, but only if the types are known one. Arrays of
unknown types are returned as represented by the database (e.g.
``{a,b,c}``). You can easily create a typecaster for :ref:`array of
unknown types <cast-array-unknown>`.
.. _adapt-tuple:
.. index::
double: Tuple; Adaptation
single: IN operator
- Python tuples are converted in a syntax suitable for the SQL :sql:`IN`
operator and to represent a composite type::
>>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30)))
'SELECT 10 IN (10, 20, 30);'
.. note::
SQL doesn't allow an empty list in the IN operator, so your code should
guard against empty tuples.
If you want PostgreSQL composite types to be converted into a Python
tuple/namedtuple you can use the `~psycopg2.extras.register_composite()`
function.
.. versionadded:: 2.0.6
the tuple :sql:`IN` adaptation.
.. versionchanged:: 2.0.14
the tuple :sql:`IN` adapter is always active. In previous releases it
was necessary to import the `~psycopg2.extensions` module to have it
registered.
.. versionchanged:: 2.3
`~collections.namedtuple` instances are adapted like regular tuples and
can thus be used to represent composite types.
.. _adapt-dict:
.. index::
single: dict; Adaptation
single: hstore; Adaptation
- Python dictionaries are converted into the |hstore|_ data type. By default
the adapter is not enabled: see `~psycopg2.extras.register_hstore()` for
further details.
.. |hstore| replace:: :sql:`hstore`
.. _hstore: http://www.postgresql.org/docs/current/static/hstore.html
.. versionadded:: 2.3
the :sql:`hstore` adaptation.
.. index:: .. index::
@ -401,7 +343,7 @@ the SQL string that would be sent to the database.
.. _unicode-handling: .. _unicode-handling:
Unicode handling Unicode handling
^^^^^^^^^^^^^^^^ ''''''''''''''''
Psycopg can exchange Unicode data with a PostgreSQL database. Python Psycopg can exchange Unicode data with a PostgreSQL database. Python
`!unicode` objects are automatically *encoded* in the client encoding `!unicode` objects are automatically *encoded* in the client encoding
@ -464,20 +406,108 @@ the connection or globally: see the function
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
and then forget about this story. and forget about this story.
.. index::
single: Buffer; Adaptation
single: bytea; Adaptation
single: bytes; Adaptation
single: bytearray; Adaptation
single: memoryview; Adaptation
single: Binary string
.. _adapt-binary:
Binary adaptation
^^^^^^^^^^^^^^^^^
Binary types: Python types representing binary objects are converted into
PostgreSQL binary string syntax, suitable for :sql:`bytea` fields. Such
types are `buffer` (only available in Python 2), `memoryview` (available
from Python 2.7), `bytearray` (available from Python 2.6) and `bytes`
(only from Python 3: the name is available from Python 2.6 but it's only an
alias for the type `!str`). Any object implementing the `Revised Buffer
Protocol`__ should be usable as binary type where the protocol is supported
(i.e. from Python 2.6). Received data is returned as `!buffer` (in Python 2)
or `!memoryview` (in Python 3).
.. __: http://www.python.org/dev/peps/pep-3118/
.. versionchanged:: 2.4
only strings were supported before.
.. versionchanged:: 2.4.1
can parse the 'hex' format from 9.0 servers without relying on the
version of the client library.
.. note::
In Python 2, if you have binary data in a `!str` object, you can pass them
to a :sql:`bytea` field using the `psycopg2.Binary` wrapper::
mypic = open('picture.png', 'rb').read()
curs.execute("insert into blobs (file) values (%s)",
(psycopg2.Binary(mypic),))
.. warning::
Since version 9.0 PostgreSQL uses by default `a new "hex" format`__ to
emit :sql:`bytea` fields. Starting from Psycopg 2.4.1 the format is
correctly supported. If you use a previous version you will need some
extra care when receiving bytea from PostgreSQL: you must have at least
libpq 9.0 installed on the client or alternatively you can set the
`bytea_output`__ configuration parameter to ``escape``, either in the
server configuration file or in the client session (using a query such as
``SET bytea_output TO escape;``) before receiving binary data.
.. __: http://www.postgresql.org/docs/current/static/datatype-binary.html
.. __: http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-BYTEA-OUTPUT
.. index::
single: Adaptation; Date/Time objects
single: Date objects; Adaptation
single: Time objects; Adaptation
single: Interval objects; Adaptation
single: mx.DateTime; Adaptation
.. _adapt-date:
Date/Time objects adaptation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Date and time objects: builtin `~datetime.datetime`, `~datetime.date`,
`~datetime.time`, `~datetime.timedelta` are converted into PostgreSQL's
:sql:`timestamp[tz]`, :sql:`date`, :sql:`time`, :sql:`interval` data types.
Time zones are supported too. The Egenix `mx.DateTime`_ objects are adapted
the same way::
>>> dt = datetime.datetime.now()
>>> dt
datetime.datetime(2010, 2, 8, 1, 40, 27, 425337)
>>> cur.mogrify("SELECT %s, %s, %s;", (dt, dt.date(), dt.time()))
"SELECT '2010-02-08T01:40:27.425337', '2010-02-08', '01:40:27.425337';"
>>> cur.mogrify("SELECT %s;", (dt - datetime.datetime(2010,1,1),))
"SELECT '38 days 6027.425337 seconds';"
.. seealso:: `PostgreSQL date/time types
<http://www.postgresql.org/docs/current/static/datatype-datetime.html>`__
.. index:: .. index::
single: Time Zones single: Time Zones
.. _tz-handling: .. _tz-handling:
Time zones handling Time zones handling
^^^^^^^^^^^^^^^^^^^ '''''''''''''''''''
The PostgreSQL type :sql:`timestamp with time zone` is converted into Python The PostgreSQL type :sql:`timestamp with time zone` (a.k.a.
`~datetime.datetime` objects with a `~datetime.datetime.tzinfo` attribute set :sql:`timestamptz`) is converted into Python `~datetime.datetime` objects with
to a `~psycopg2.tz.FixedOffsetTimezone` instance. a `~datetime.datetime.tzinfo` attribute set to a
`~psycopg2.tz.FixedOffsetTimezone` instance.
>>> cur.execute("SET TIME ZONE 'Europe/Rome';") # UTC + 1 hour >>> cur.execute("SET TIME ZONE 'Europe/Rome';") # UTC + 1 hour
>>> cur.execute("SELECT '2010-01-01 10:30:45'::timestamptz;") >>> cur.execute("SELECT '2010-01-01 10:30:45'::timestamptz;")
@ -500,6 +530,81 @@ rounded to the nearest minute, with an error of up to 30 seconds.
versions use `psycopg2.extras.register_tstz_w_secs()`. versions use `psycopg2.extras.register_tstz_w_secs()`.
.. _adapt-list:
Lists adaptation
^^^^^^^^^^^^^^^^
.. index::
single: Array; Adaptation
double: Lists; Adaptation
Python lists are converted into PostgreSQL :sql:`ARRAY`\ s::
>>> cur.mogrify("SELECT %s;", ([10, 20, 30], ))
'SELECT ARRAY[10,20,30];'
.. note::
You can use a Python list as the argument of the :sql:`IN` operator using
`the PostgreSQL ANY operator`__. ::
ids = [10, 20, 30]
cur.execute("SELECT * FROM data WHERE id = ANY(%s);", (ids,))
Furthermore :sql:`ANY` can also work with empty lists, whereas :sql:`IN ()`
is a SQL syntax error.
.. __: http://www.postgresql.org/docs/current/static/functions-subquery.html#FUNCTIONS-SUBQUERY-ANY-SOME
.. note::
Reading back from PostgreSQL, arrays are converted to lists of Python
objects as expected, but only if the items are of a known known type.
Arrays of unknown types are returned as represented by the database (e.g.
``{a,b,c}``). If you want to convert the items into Python objects you can
easily create a typecaster for :ref:`array of unknown types
<cast-array-unknown>`.
.. _adapt-tuple:
Tuples adaptation
^^^^^^^^^^^^^^^^^^
.. index::
double: Tuple; Adaptation
single: IN operator
Python tuples are converted in a syntax suitable for the SQL :sql:`IN`
operator and to represent a composite type::
>>> cur.mogrify("SELECT %s IN %s;", (10, (10, 20, 30)))
'SELECT 10 IN (10, 20, 30);'
.. note::
SQL doesn't allow an empty list in the :sql:`IN` operator, so your code
should guard against empty tuples. Alternatively you can :ref:`use a
Python list <adapt-list>`.
If you want PostgreSQL composite types to be converted into a Python
tuple/namedtuple you can use the `~psycopg2.extras.register_composite()`
function.
.. versionadded:: 2.0.6
the tuple :sql:`IN` adaptation.
.. versionchanged:: 2.0.14
the tuple :sql:`IN` adapter is always active. In previous releases it
was necessary to import the `~psycopg2.extensions` module to have it
registered.
.. versionchanged:: 2.3
`~collections.namedtuple` instances are adapted like regular tuples and
can thus be used to represent composite types.
.. index:: Transaction, Begin, Commit, Rollback, Autocommit, Read only .. index:: Transaction, Begin, Commit, Rollback, Autocommit, Read only
.. _transactions-control: .. _transactions-control:
@ -527,7 +632,7 @@ It is possible to set the connection in *autocommit* mode: this way all the
commands executed will be immediately committed and no rollback is possible. A commands executed will be immediately committed and no rollback is possible. A
few commands (e.g. :sql:`CREATE DATABASE`, :sql:`VACUUM`...) require to be run few commands (e.g. :sql:`CREATE DATABASE`, :sql:`VACUUM`...) require to be run
outside any transaction: in order to be able to run these commands from outside any transaction: in order to be able to run these commands from
Psycopg, the session must be in autocommit mode: you can use the Psycopg, the connection must be in autocommit mode: you can use the
`~connection.autocommit` property (`~connection.set_isolation_level()` in `~connection.autocommit` property (`~connection.set_isolation_level()` in
older versions). older versions).
@ -546,6 +651,30 @@ change the isolation level. See the `~connection.set_session()` method for all
the details. the details.
.. index::
single: with statement
``with`` statement
^^^^^^^^^^^^^^^^^^
Starting from version 2.5, psycopg2's connections and cursors are *context
managers* and can be used with the ``with`` statement::
with psycopg2.connect(DSN) as conn:
with conn.cursor() as curs:
curs.execute(SQL)
When a connection exits the ``with`` block, if no exception has been raised by
the block, the transaction is committed. In case of exception the transaction
is rolled back. In no case the connection is closed: a connection can be used
in more than a ``with`` statement and each ``with`` block is effectively
wrapped in a transaction.
When a cursor exits the ``with`` block it is closed, releasing any resource
eventually associated with it. The state of the transaction is not affected.
.. index:: .. index::
pair: Server side; Cursor pair: Server side; Cursor
pair: Named; Cursor pair: Named; Cursor
@ -576,7 +705,9 @@ cursor is created using the `~connection.cursor()` method specifying the
*name* parameter. Such cursor will behave mostly like a regular cursor, *name* parameter. Such cursor will behave mostly like a regular cursor,
allowing the user to move in the dataset using the `~cursor.scroll()` allowing the user to move in the dataset using the `~cursor.scroll()`
method and to read the data using `~cursor.fetchone()` and method and to read the data using `~cursor.fetchone()` and
`~cursor.fetchmany()` methods. `~cursor.fetchmany()` methods. Normally you can only scroll forward in a
cursor: if you need to scroll backwards you should declare your cursor
`~cursor.scrollable`.
Named cursors are also :ref:`iterable <cursor-iterable>` like regular cursors. Named cursors are also :ref:`iterable <cursor-iterable>` like regular cursors.
Note however that before Psycopg 2.4 iteration was performed fetching one Note however that before Psycopg 2.4 iteration was performed fetching one

View File

@ -41,23 +41,6 @@ Homepage: http://initd.org/projects/psycopg2
# Import modules needed by _psycopg to allow tools like py2exe to do # Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies. # their work without bothering about the module dependencies.
import sys, warnings
if sys.version_info >= (2, 3):
try:
import datetime as _psycopg_needs_datetime
except:
warnings.warn(
"can't import datetime module probably needed by _psycopg",
RuntimeWarning)
if sys.version_info >= (2, 4):
try:
import decimal as _psycopg_needs_decimal
except:
warnings.warn(
"can't import decimal module probably needed by _psycopg",
RuntimeWarning)
del sys, warnings
# Note: the first internal import should be _psycopg, otherwise the real cause # Note: the first internal import should be _psycopg, otherwise the real cause
# of a failed loading of the C module may get hidden, see # of a failed loading of the C module may get hidden, see
# http://archives.postgresql.org/psycopg/2011-02/msg00044.php # http://archives.postgresql.org/psycopg/2011-02/msg00044.php
@ -118,7 +101,7 @@ del re
def connect(dsn=None, def connect(dsn=None,
database=None, user=None, password=None, host=None, port=None, database=None, user=None, password=None, host=None, port=None,
connection_factory=None, async=False, **kwargs): connection_factory=None, cursor_factory=None, async=False, **kwargs):
""" """
Create a new database connection. Create a new database connection.
@ -143,6 +126,9 @@ def connect(dsn=None,
factory can be specified. It should be a callable object taking a dsn factory can be specified. It should be a callable object taking a dsn
argument. argument.
Using the *cursor_factory* parameter, a new default cursor factory will be
used by cursor().
Using *async*=True an asynchronous connection will be created. Using *async*=True an asynchronous connection will be created.
Any other keyword parameter will be passed to the underlying client Any other keyword parameter will be passed to the underlying client
@ -175,8 +161,8 @@ def connect(dsn=None,
dsn = " ".join(["%s=%s" % (k, _param_escape(str(v))) dsn = " ".join(["%s=%s" % (k, _param_escape(str(v)))
for (k, v) in items]) for (k, v) in items])
return _connect(dsn, connection_factory=connection_factory, async=async) conn = _connect(dsn, connection_factory=connection_factory, async=async)
if cursor_factory is not None:
conn.cursor_factory = cursor_factory
__all__ = filter(lambda k: not k.startswith('_'), locals().keys())
return conn

194
lib/_json.py Normal file
View File

@ -0,0 +1,194 @@
"""Implementation of the JSON adaptation objects
This module exists to avoid a circular import problem: pyscopg2.extras depends
on psycopg2.extension, so I can't create the default JSON typecasters in
extensions importing register_json from extras.
"""
# psycopg/_json.py - Implementation of the JSON adaptation objects
#
# Copyright (C) 2012 Daniele Varrazzo <daniele.varrazzo@gmail.com>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link this program with the OpenSSL library (or with
# modified versions of OpenSSL that use the same license as OpenSSL),
# and distribute linked combinations including the two.
#
# You must obey the GNU Lesser General Public License in all respects for
# all of the code used other than OpenSSL.
#
# psycopg2 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 Lesser General Public
# License for more details.
import sys
from psycopg2._psycopg import ISQLQuote, QuotedString
from psycopg2._psycopg import new_type, new_array_type, register_type
# import the best json implementation available
if sys.version_info[:2] >= (2,6):
import json
else:
try:
import simplejson as json
except ImportError:
json = None
# oids from PostgreSQL 9.2
JSON_OID = 114
JSONARRAY_OID = 199
class Json(object):
"""
An `~psycopg2.extensions.ISQLQuote` wrapper to adapt a Python object to
:sql:`json` data type.
`!Json` can be used to wrap any object supported by the provided *dumps*
function. If none is provided, the standard :py:func:`json.dumps()` is
used (`!simplejson` for Python < 2.6;
`~psycopg2.extensions.ISQLQuote.getquoted()` will raise `!ImportError` if
the module is not available).
"""
def __init__(self, adapted, dumps=None):
self.adapted = adapted
if dumps is not None:
self._dumps = dumps
elif json is not None:
self._dumps = json.dumps
else:
self._dumps = None
def __conform__(self, proto):
if proto is ISQLQuote:
return self
def dumps(self, obj):
"""Serialize *obj* in JSON format.
The default is to call `!json.dumps()` or the *dumps* function
provided in the constructor. You can override this method to create a
customized JSON wrapper.
"""
dumps = self._dumps
if dumps is not None:
return dumps(obj)
else:
raise ImportError(
"json module not available: "
"you should provide a dumps function")
def getquoted(self):
s = self.dumps(self.adapted)
return QuotedString(s).getquoted()
def register_json(conn_or_curs=None, globally=False, loads=None,
oid=None, array_oid=None):
"""Create and register typecasters converting :sql:`json` type to Python objects.
:param conn_or_curs: a connection or cursor used to find the :sql:`json`
and :sql:`json[]` oids; the typecasters are registered in a scope
limited to this object, unless *globally* is set to `!True`. It can be
`!None` if the oids are provided
:param globally: if `!False` register the typecasters only on
*conn_or_curs*, otherwise register them globally
:param loads: the function used to parse the data into a Python object. If
`!None` use `!json.loads()`, where `!json` is the module chosen
according to the Python version (see above)
:param oid: the OID of the :sql:`json` type if known; If not, it will be
queried on *conn_or_curs*
:param array_oid: the OID of the :sql:`json[]` array type if known;
if not, it will be queried on *conn_or_curs*
The connection or cursor passed to the function will be used to query the
database and look for the OID of the :sql:`json` type. No query is
performed if *oid* and *array_oid* are provided. Raise
`~psycopg2.ProgrammingError` if the type is not found.
"""
if oid is None:
oid, array_oid = _get_json_oids(conn_or_curs)
JSON, JSONARRAY = _create_json_typecasters(oid, array_oid, loads)
register_type(JSON, not globally and conn_or_curs or None)
if JSONARRAY is not None:
register_type(JSONARRAY, not globally and conn_or_curs or None)
return JSON, JSONARRAY
def register_default_json(conn_or_curs=None, globally=False, loads=None):
"""
Create and register :sql:`json` typecasters for PostgreSQL 9.2 and following.
Since PostgreSQL 9.2 :sql:`json` is a builtin type, hence its oid is known
and fixed. This function allows specifying a customized *loads* function
for the default :sql:`json` type without querying the database.
All the parameters have the same meaning of `register_json()`.
"""
return register_json(conn_or_curs=conn_or_curs, globally=globally,
loads=loads, oid=JSON_OID, array_oid=JSONARRAY_OID)
def _create_json_typecasters(oid, array_oid, loads=None):
"""Create typecasters for json data type."""
if loads is None:
if json is None:
raise ImportError("no json module available")
else:
loads = json.loads
def typecast_json(s, cur):
if s is None:
return None
return loads(s)
JSON = new_type((oid, ), 'JSON', typecast_json)
if array_oid is not None:
JSONARRAY = new_array_type((array_oid, ), "JSONARRAY", JSON)
else:
JSONARRAY = None
return JSON, JSONARRAY
def _get_json_oids(conn_or_curs):
# lazy imports
from psycopg2.extensions import STATUS_IN_TRANSACTION
from psycopg2.extras import _solve_conn_curs
conn, curs = _solve_conn_curs(conn_or_curs)
# Store the transaction status of the connection to revert it after use
conn_status = conn.status
# column typarray not available before PG 8.3
typarray = conn.server_version >= 80300 and "typarray" or "NULL"
# get the oid for the hstore
curs.execute(
"SELECT t.oid, %s FROM pg_type t WHERE t.typname = 'json';"
% typarray)
r = curs.fetchone()
# revert the status of the connection as before the command
if (conn_status != STATUS_IN_TRANSACTION and not conn.autocommit):
conn.rollback()
if not r:
raise conn.ProgrammingError("json data type not found")
return r

468
lib/_range.py Normal file
View File

@ -0,0 +1,468 @@
"""Implementation of the Range type and adaptation
"""
# psycopg/_range.py - Implementation of the Range type and adaptation
#
# Copyright (C) 2012 Daniele Varrazzo <daniele.varrazzo@gmail.com>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link this program with the OpenSSL library (or with
# modified versions of OpenSSL that use the same license as OpenSSL),
# and distribute linked combinations including the two.
#
# You must obey the GNU Lesser General Public License in all respects for
# all of the code used other than OpenSSL.
#
# psycopg2 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 Lesser General Public
# License for more details.
import re
from psycopg2._psycopg import ProgrammingError, InterfaceError
from psycopg2.extensions import ISQLQuote, adapt, register_adapter, b
from psycopg2.extensions import new_type, new_array_type, register_type
class Range(object):
"""Python representation for a PostgreSQL |range|_ type.
:param lower: lower bound for the range. `!None` means unbound
:param upper: upper bound for the range. `!None` means unbound
:param bounds: one of the literal strings ``()``, ``[)``, ``(]``, ``[]``,
representing whether the lower or upper bounds are included
:param empty: if `!True`, the range is empty
"""
__slots__ = ('_lower', '_upper', '_bounds')
def __init__(self, lower=None, upper=None, bounds='[)', empty=False):
if not empty:
if bounds not in ('[)', '(]', '()', '[]'):
raise ValueError("bound flags not valid: %r" % bounds)
self._lower = lower
self._upper = upper
self._bounds = bounds
else:
self._lower = self._upper = self._bounds = None
def __repr__(self):
if self._bounds is None:
return "%s(empty=True)" % self.__class__.__name__
else:
return "%s(%r, %r, %r)" % (self.__class__.__name__,
self._lower, self._upper, self._bounds)
@property
def lower(self):
"""The lower bound of the range. `!None` if empty or unbound."""
return self._lower
@property
def upper(self):
"""The upper bound of the range. `!None` if empty or unbound."""
return self._upper
@property
def isempty(self):
"""`!True` if the range is empty."""
return self._bounds is None
@property
def lower_inf(self):
"""`!True` if the range doesn't have a lower bound."""
if self._bounds is None: return False
return self._lower is None
@property
def upper_inf(self):
"""`!True` if the range doesn't have an upper bound."""
if self._bounds is None: return False
return self._upper is None
@property
def lower_inc(self):
"""`!True` if the lower bound is included in the range."""
if self._bounds is None: return False
if self._lower is None: return False
return self._bounds[0] == '['
@property
def upper_inc(self):
"""`!True` if the upper bound is included in the range."""
if self._bounds is None: return False
if self._upper is None: return False
return self._bounds[1] == ']'
def __contains__(self, x):
if self._bounds is None: return False
if self._lower is not None:
if self._bounds[0] == '[':
if x < self._lower: return False
else:
if x <= self._lower: return False
if self._upper is not None:
if self._bounds[1] == ']':
if x > self._upper: return False
else:
if x >= self._upper: return False
return True
def __nonzero__(self):
return self._bounds is not None
def __eq__(self, other):
return (self._lower == other._lower
and self._upper == other._upper
and self._bounds == other._bounds)
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self._lower, self._upper, self._bounds))
def __lt__(self, other):
raise TypeError(
'Range objects cannot be ordered; please refer to the PostgreSQL'
' documentation to perform this operation in the database')
__le__ = __gt__ = __ge__ = __lt__
def register_range(pgrange, pyrange, conn_or_curs, globally=False):
"""Create and register an adapter and the typecasters to convert between
a PostgreSQL |range|_ type and a PostgreSQL `Range` subclass.
:param pgrange: the name of the PostgreSQL |range| type. Can be
schema-qualified
:param pyrange: a `Range` strict subclass, or just a name to give to a new
class
:param conn_or_curs: a connection or cursor used to find the oid of the
range and its subtype; the typecaster is registered in a scope limited
to this object, unless *globally* is set to `!True`
:param globally: if `!False` (default) register the typecaster only on
*conn_or_curs*, otherwise register it globally
:return: `RangeCaster` instance responsible for the conversion
If a string is passed to *pyrange*, a new `Range` subclass is created
with such name and will be available as the `~RangeCaster.range` attribute
of the returned `RangeCaster` object.
The function queries the database on *conn_or_curs* to inspect the
*pgrange* type and raises `~psycopg2.ProgrammingError` if the type is not
found. If querying the database is not advisable, use directly the
`RangeCaster` class and register the adapter and typecasters using the
provided functions.
"""
caster = RangeCaster._from_db(pgrange, pyrange, conn_or_curs)
caster._register(not globally and conn_or_curs or None)
return caster
class RangeAdapter(object):
"""`ISQLQuote` adapter for `Range` subclasses.
This is an abstract class: concrete classes must set a `name` class
attribute or override `getquoted()`.
"""
name = None
def __init__(self, adapted):
self.adapted = adapted
def __conform__(self, proto):
if self._proto is ISQLQuote:
return self
def prepare(self, conn):
self._conn = conn
def getquoted(self):
if self.name is None:
raise NotImplementedError(
'RangeAdapter must be subclassed overriding its name '
'or the getquoted() method')
r = self.adapted
if r.isempty:
return b("'empty'::" + self.name)
if r.lower is not None:
a = adapt(r.lower)
if hasattr(a, 'prepare'):
a.prepare(self._conn)
lower = a.getquoted()
else:
lower = b('NULL')
if r.upper is not None:
a = adapt(r.upper)
if hasattr(a, 'prepare'):
a.prepare(self._conn)
upper = a.getquoted()
else:
upper = b('NULL')
return b(self.name + '(') + lower + b(', ') + upper \
+ b(", '%s')" % r._bounds)
class RangeCaster(object):
"""Helper class to convert between `Range` and PostgreSQL range types.
Objects of this class are usually created by `register_range()`. Manual
creation could be useful if querying the database is not advisable: in
this case the oids must be provided.
"""
def __init__(self, pgrange, pyrange, oid, subtype_oid, array_oid=None):
self.subtype_oid = subtype_oid
self._create_ranges(pgrange, pyrange)
name = self.adapter.name or self.adapter.__class__.__name__
self.typecaster = new_type((oid,), name, self.parse)
if array_oid is not None:
self.array_typecaster = new_array_type(
(array_oid,), name + "ARRAY", self.typecaster)
else:
self.array_typecaster = None
def _create_ranges(self, pgrange, pyrange):
"""Create Range and RangeAdapter classes if needed."""
# if got a string create a new RangeAdapter concrete type (with a name)
# else take it as an adapter. Passing an adapter should be considered
# an implementation detail and is not documented. It is currently used
# for the numeric ranges.
self.adapter = None
if isinstance(pgrange, basestring):
self.adapter = type(pgrange, (RangeAdapter,), {})
self.adapter.name = pgrange
else:
try:
if issubclass(pgrange, RangeAdapter) and pgrange is not RangeAdapter:
self.adapter = pgrange
except TypeError:
pass
if self.adapter is None:
raise TypeError(
'pgrange must be a string or a RangeAdapter strict subclass')
self.range = None
try:
if isinstance(pyrange, basestring):
self.range = type(pyrange, (Range,), {})
if issubclass(pyrange, Range) and pyrange is not Range:
self.range = pyrange
except TypeError:
pass
if self.range is None:
raise TypeError(
'pyrange must be a type or a Range strict subclass')
@classmethod
def _from_db(self, name, pyrange, conn_or_curs):
"""Return a `RangeCaster` instance for the type *pgrange*.
Raise `ProgrammingError` if the type is not found.
"""
from psycopg2.extensions import STATUS_IN_TRANSACTION
from psycopg2.extras import _solve_conn_curs
conn, curs = _solve_conn_curs(conn_or_curs)
if conn.server_version < 90200:
raise ProgrammingError("range types not available in version %s"
% conn.server_version)
# Store the transaction status of the connection to revert it after use
conn_status = conn.status
# Use the correct schema
if '.' in name:
schema, tname = name.split('.', 1)
else:
tname = name
schema = 'public'
# get the type oid and attributes
try:
curs.execute("""\
select rngtypid, rngsubtype,
(select typarray from pg_type where oid = rngtypid)
from pg_range r
join pg_type t on t.oid = rngtypid
join pg_namespace ns on ns.oid = typnamespace
where typname = %s and ns.nspname = %s;
""", (tname, schema))
except ProgrammingError:
if not conn.autocommit:
conn.rollback()
raise
else:
rec = curs.fetchone()
# revert the status of the connection as before the command
if (conn_status != STATUS_IN_TRANSACTION
and not conn.autocommit):
conn.rollback()
if not rec:
raise ProgrammingError(
"PostgreSQL type '%s' not found" % name)
type, subtype, array = rec
return RangeCaster(name, pyrange,
oid=type, subtype_oid=subtype, array_oid=array)
_re_range = re.compile(r"""
( \(|\[ ) # lower bound flag
(?: # lower bound:
" ( (?: [^"] | "")* ) " # - a quoted string
| ( [^",]+ ) # - or an unquoted string
)? # - or empty (not catched)
,
(?: # upper bound:
" ( (?: [^"] | "")* ) " # - a quoted string
| ( [^"\)\]]+ ) # - or an unquoted string
)? # - or empty (not catched)
( \)|\] ) # upper bound flag
""", re.VERBOSE)
_re_undouble = re.compile(r'(["\\])\1')
def parse(self, s, cur=None):
if s is None:
return None
if s == 'empty':
return self.range(empty=True)
m = self._re_range.match(s)
if m is None:
raise InterfaceError("failed to parse range: %s")
lower = m.group(3)
if lower is None:
lower = m.group(2)
if lower is not None:
lower = self._re_undouble.sub(r"\1", lower)
upper = m.group(5)
if upper is None:
upper = m.group(4)
if upper is not None:
upper = self._re_undouble.sub(r"\1", upper)
if cur is not None:
lower = cur.cast(self.subtype_oid, lower)
upper = cur.cast(self.subtype_oid, upper)
bounds = m.group(1) + m.group(6)
return self.range(lower, upper, bounds)
def _register(self, scope=None):
register_type(self.typecaster, scope)
if self.array_typecaster is not None:
register_type(self.array_typecaster, scope)
register_adapter(self.range, self.adapter)
class NumericRange(Range):
"""A `Range` suitable to pass Python numeric types to a PostgreSQL range.
PostgreSQL types :sql:`int4range`, :sql:`int8range`, :sql:`numrange` are
casted into `!NumericRange` instances.
"""
pass
class DateRange(Range):
"""Represents :sql:`daterange` values."""
pass
class DateTimeRange(Range):
"""Represents :sql:`tsrange` values."""
pass
class DateTimeTZRange(Range):
"""Represents :sql:`tstzrange` values."""
pass
# Special adaptation for NumericRange. Allows to pass number range regardless
# of whether they are ints, floats and what size of ints are, which are
# pointless in Python world. On the way back, no numeric range is casted to
# NumericRange, but only to their subclasses
class NumberRangeAdapter(RangeAdapter):
"""Adapt a range if the subtype doesn't need quotes."""
def getquoted(self):
r = self.adapted
if r.isempty:
return "'empty'"
if not r.lower_inf:
# not exactly: we are relying that none of these object is really
# quoted (they are numbers). Also, I'm lazy and not preparing the
# adapter because I assume encoding doesn't matter for these
# objects.
lower = adapt(r.lower).getquoted().decode('ascii')
else:
lower = ''
if not r.upper_inf:
upper = adapt(r.upper).getquoted().decode('ascii')
else:
upper = ''
return b("'%s%s,%s%s'" % (
r._bounds[0], lower, upper, r._bounds[1]))
# TODO: probably won't work with infs, nans and other tricky cases.
register_adapter(NumericRange, NumberRangeAdapter)
# Register globally typecasters and adapters for builtin range types.
# note: the adapter is registered more than once, but this is harmless.
int4range_caster = RangeCaster(NumberRangeAdapter, NumericRange,
oid=3904, subtype_oid=23, array_oid=3905)
int4range_caster._register()
int8range_caster = RangeCaster(NumberRangeAdapter, NumericRange,
oid=3926, subtype_oid=20, array_oid=3927)
int8range_caster._register()
numrange_caster = RangeCaster(NumberRangeAdapter, NumericRange,
oid=3906, subtype_oid=1700, array_oid=3907)
numrange_caster._register()
daterange_caster = RangeCaster('daterange', DateRange,
oid=3912, subtype_oid=1082, array_oid=3913)
daterange_caster._register()
tsrange_caster = RangeCaster('tsrange', DateTimeRange,
oid=3908, subtype_oid=1114, array_oid=3909)
tsrange_caster._register()
tstzrange_caster = RangeCaster('tstzrange', DateTimeTZRange,
oid=3910, subtype_oid=1184, array_oid=3911)
tstzrange_caster._register()

View File

@ -59,6 +59,7 @@ CLASS_INVALID_TRANSACTION_INITIATION = '0B'
CLASS_LOCATOR_EXCEPTION = '0F' CLASS_LOCATOR_EXCEPTION = '0F'
CLASS_INVALID_GRANTOR = '0L' CLASS_INVALID_GRANTOR = '0L'
CLASS_INVALID_ROLE_SPECIFICATION = '0P' CLASS_INVALID_ROLE_SPECIFICATION = '0P'
CLASS_DIAGNOSTICS_EXCEPTION = '0Z'
CLASS_CASE_NOT_FOUND = '20' CLASS_CASE_NOT_FOUND = '20'
CLASS_CARDINALITY_VIOLATION = '21' CLASS_CARDINALITY_VIOLATION = '21'
CLASS_DATA_EXCEPTION = '22' CLASS_DATA_EXCEPTION = '22'
@ -139,6 +140,10 @@ INVALID_GRANT_OPERATION = '0LP01'
# Class 0P - Invalid Role Specification # Class 0P - Invalid Role Specification
INVALID_ROLE_SPECIFICATION = '0P000' INVALID_ROLE_SPECIFICATION = '0P000'
# Class 0Z - Diagnostics Exception
DIAGNOSTICS_EXCEPTION = '0Z000'
STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER = '0Z002'
# Class 20 - Case Not Found # Class 20 - Case Not Found
CASE_NOT_FOUND = '20000' CASE_NOT_FOUND = '20000'
@ -331,6 +336,7 @@ INSUFFICIENT_RESOURCES = '53000'
DISK_FULL = '53100' DISK_FULL = '53100'
OUT_OF_MEMORY = '53200' OUT_OF_MEMORY = '53200'
TOO_MANY_CONNECTIONS = '53300' TOO_MANY_CONNECTIONS = '53300'
CONFIGURATION_LIMIT_EXCEEDED = '53400'
# Class 54 - Program Limit Exceeded # Class 54 - Program Limit Exceeded
PROGRAM_LIMIT_EXCEEDED = '54000' PROGRAM_LIMIT_EXCEEDED = '54000'
@ -353,6 +359,7 @@ CANNOT_CONNECT_NOW = '57P03'
DATABASE_DROPPED = '57P04' DATABASE_DROPPED = '57P04'
# Class 58 - System Error (errors external to PostgreSQL itself) # Class 58 - System Error (errors external to PostgreSQL itself)
SYSTEM_ERROR = '58000'
IO_ERROR = '58030' IO_ERROR = '58030'
UNDEFINED_FILE = '58P01' UNDEFINED_FILE = '58P01'
DUPLICATE_FILE = '58P02' DUPLICATE_FILE = '58P02'

View File

@ -58,7 +58,7 @@ except ImportError:
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid
from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type
from psycopg2._psycopg import ISQLQuote, Notify from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
@ -151,6 +151,21 @@ class NoneAdapter(object):
return _null return _null
# Create default json typecasters for PostgreSQL 9.2 oids
from psycopg2._json import register_default_json
try:
JSON, JSONARRAY = register_default_json()
except ImportError:
pass
del register_default_json
# Create default Range typecasters
from psycopg2. _range import Range
del Range
# Add the "cleaned" version of the encodings to the key. # Add the "cleaned" version of the encodings to the key.
# When the encoding is set its name is cleaned up from - and _ and turned # When the encoding is set its name is cleaned up from - and _ and turned
# uppercase, so an encoding not respecting these rules wouldn't be found in the # uppercase, so an encoding not respecting these rules wouldn't be found in the
@ -160,5 +175,3 @@ for k, v in encodings.items():
encodings[k] = v encodings[k] = v
del k, v del k, v
__all__ = filter(lambda k: not k.startswith('_'), locals().keys())

View File

@ -25,16 +25,15 @@ and classes untill a better place in the distribution is found.
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details. # License for more details.
import os import os as _os
import sys import sys as _sys
import time import time as _time
import warnings import re as _re
import re as regex
try: try:
import logging import logging as _logging
except: except:
logging = None _logging = None
import psycopg2 import psycopg2
from psycopg2 import extensions as _ext from psycopg2 import extensions as _ext
@ -192,7 +191,7 @@ class DictRow(list):
self._index = data[1] self._index = data[1]
# drop the crusty Py2 methods # drop the crusty Py2 methods
if sys.version_info[0] > 2: if _sys.version_info[0] > 2:
items = iteritems; del iteritems items = iteritems; del iteritems
keys = iterkeys; del iterkeys keys = iterkeys; del iterkeys
values = itervalues; del itervalues values = itervalues; del itervalues
@ -302,21 +301,21 @@ class NamedTupleCursor(_cursor):
nt = self.Record nt = self.Record
if nt is None: if nt is None:
nt = self.Record = self._make_nt() nt = self.Record = self._make_nt()
return nt(*t) return nt._make(t)
def fetchmany(self, size=None): def fetchmany(self, size=None):
ts = super(NamedTupleCursor, self).fetchmany(size) ts = super(NamedTupleCursor, self).fetchmany(size)
nt = self.Record nt = self.Record
if nt is None: if nt is None:
nt = self.Record = self._make_nt() nt = self.Record = self._make_nt()
return [nt(*t) for t in ts] return map(nt._make, ts)
def fetchall(self): def fetchall(self):
ts = super(NamedTupleCursor, self).fetchall() ts = super(NamedTupleCursor, self).fetchall()
nt = self.Record nt = self.Record
if nt is None: if nt is None:
nt = self.Record = self._make_nt() nt = self.Record = self._make_nt()
return [nt(*t) for t in ts] return map(nt._make, ts)
def __iter__(self): def __iter__(self):
it = super(NamedTupleCursor, self).__iter__() it = super(NamedTupleCursor, self).__iter__()
@ -326,10 +325,10 @@ class NamedTupleCursor(_cursor):
if nt is None: if nt is None:
nt = self.Record = self._make_nt() nt = self.Record = self._make_nt()
yield nt(*t) yield nt._make(t)
while 1: while 1:
yield nt(*it.next()) yield nt._make(it.next())
try: try:
from collections import namedtuple from collections import namedtuple
@ -354,7 +353,7 @@ class LoggingConnection(_connection):
instance from the standard logging module. instance from the standard logging module.
""" """
self._logobj = logobj self._logobj = logobj
if logging and isinstance(logobj, logging.Logger): if _logging and isinstance(logobj, _logging.Logger):
self.log = self._logtologger self.log = self._logtologger
else: else:
self.log = self._logtofile self.log = self._logtofile
@ -370,7 +369,7 @@ class LoggingConnection(_connection):
def _logtofile(self, msg, curs): def _logtofile(self, msg, curs):
msg = self.filter(msg, curs) msg = self.filter(msg, curs)
if msg: self._logobj.write(msg + os.linesep) if msg: self._logobj.write(msg + _os.linesep)
def _logtologger(self, msg, curs): def _logtologger(self, msg, curs):
msg = self.filter(msg, curs) msg = self.filter(msg, curs)
@ -418,9 +417,9 @@ class MinTimeLoggingConnection(LoggingConnection):
self._mintime = mintime self._mintime = mintime
def filter(self, msg, curs): def filter(self, msg, curs):
t = (time.time() - curs.timestamp) * 1000 t = (_time.time() - curs.timestamp) * 1000
if t > self._mintime: if t > self._mintime:
return msg + os.linesep + " (execution time: %d ms)" % t return msg + _os.linesep + " (execution time: %d ms)" % t
def cursor(self, *args, **kwargs): def cursor(self, *args, **kwargs):
kwargs.setdefault('cursor_factory', MinTimeLoggingCursor) kwargs.setdefault('cursor_factory', MinTimeLoggingCursor)
@ -430,11 +429,11 @@ class MinTimeLoggingCursor(LoggingCursor):
"""The cursor sub-class companion to `MinTimeLoggingConnection`.""" """The cursor sub-class companion to `MinTimeLoggingConnection`."""
def execute(self, query, vars=None): def execute(self, query, vars=None):
self.timestamp = time.time() self.timestamp = _time.time()
return LoggingCursor.execute(self, query, vars) return LoggingCursor.execute(self, query, vars)
def callproc(self, procname, vars=None): def callproc(self, procname, vars=None):
self.timestamp = time.time() self.timestamp = _time.time()
return LoggingCursor.execute(self, procname, vars) return LoggingCursor.execute(self, procname, vars)
@ -558,20 +557,21 @@ def register_tstz_w_secs(oids=None, conn_or_curs=None):
These are now correctly handled by the default type caster, so currently These are now correctly handled by the default type caster, so currently
the function doesn't do anything. the function doesn't do anything.
""" """
import warnings
warnings.warn("deprecated", DeprecationWarning) warnings.warn("deprecated", DeprecationWarning)
import select
from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE
from psycopg2 import OperationalError
def wait_select(conn): def wait_select(conn):
"""Wait until a connection or cursor has data available. """Wait until a connection or cursor has data available.
The function is an example of a wait callback to be registered with The function is an example of a wait callback to be registered with
`~psycopg2.extensions.set_wait_callback()`. This function uses `!select()` `~psycopg2.extensions.set_wait_callback()`. This function uses
to wait for data available. :py:func:`~select.select()` to wait for data available.
""" """
import select
from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE
while 1: while 1:
state = conn.poll() state = conn.poll()
if state == POLL_OK: if state == POLL_OK:
@ -581,11 +581,14 @@ def wait_select(conn):
elif state == POLL_WRITE: elif state == POLL_WRITE:
select.select([], [conn.fileno()], []) select.select([], [conn.fileno()], [])
else: else:
raise OperationalError("bad state from poll: %s" % state) raise conn.OperationalError("bad state from poll: %s" % state)
def _solve_conn_curs(conn_or_curs): def _solve_conn_curs(conn_or_curs):
"""Return the connection and a DBAPI cursor from a connection or cursor.""" """Return the connection and a DBAPI cursor from a connection or cursor."""
if conn_or_curs is None:
raise psycopg2.ProgrammingError("no connection or cursor provided")
if hasattr(conn_or_curs, 'execute'): if hasattr(conn_or_curs, 'execute'):
conn = conn_or_curs.connection conn = conn_or_curs.connection
curs = conn.cursor(cursor_factory=_cursor) curs = conn.cursor(cursor_factory=_cursor)
@ -645,7 +648,7 @@ class HstoreAdapter(object):
getquoted = _getquoted_9 getquoted = _getquoted_9
_re_hstore = regex.compile(r""" _re_hstore = _re.compile(r"""
# hstore key: # hstore key:
# a string of normal or escaped chars # a string of normal or escaped chars
"((?: [^"\\] | \\. )*)" "((?: [^"\\] | \\. )*)"
@ -656,10 +659,10 @@ class HstoreAdapter(object):
| "((?: [^"\\] | \\. )*)" | "((?: [^"\\] | \\. )*)"
) )
(?:\s*,\s*|$) # pairs separated by comma or end of string. (?:\s*,\s*|$) # pairs separated by comma or end of string.
""", regex.VERBOSE) """, _re.VERBOSE)
@classmethod @classmethod
def parse(self, s, cur, _bsdec=regex.compile(r"\\(.)")): def parse(self, s, cur, _bsdec=_re.compile(r"\\(.)")):
"""Parse an hstore representation in a Python string. """Parse an hstore representation in a Python string.
The hstore is represented as something like:: The hstore is represented as something like::
@ -783,7 +786,7 @@ def register_hstore(conn_or_curs, globally=False, unicode=False,
array_oid = tuple([x for x in array_oid if x]) array_oid = tuple([x for x in array_oid if x])
# create and register the typecaster # create and register the typecaster
if sys.version_info[0] < 3 and unicode: if _sys.version_info[0] < 3 and unicode:
cast = HstoreAdapter.parse_unicode cast = HstoreAdapter.parse_unicode
else: else:
cast = HstoreAdapter.parse cast = HstoreAdapter.parse
@ -805,35 +808,10 @@ class CompositeCaster(object):
querying the database at registration time is not desirable (such as when querying the database at registration time is not desirable (such as when
using an :ref:`asynchronous connections <async-support>`). using an :ref:`asynchronous connections <async-support>`).
.. attribute:: name
The name of the PostgreSQL type.
.. attribute:: oid
The oid of the PostgreSQL type.
.. attribute:: array_oid
The oid of the PostgreSQL array type, if available.
.. attribute:: type
The type of the Python objects returned. If :py:func:`collections.namedtuple()`
is available, it is a named tuple with attributes equal to the type
components. Otherwise it is just the `!tuple` object.
.. attribute:: attnames
List of component names of the type to be casted.
.. attribute:: atttypes
List of component type oids of the type to be casted.
""" """
def __init__(self, name, oid, attrs, array_oid=None): def __init__(self, name, oid, attrs, array_oid=None, schema=None):
self.name = name self.name = name
self.schema = schema
self.oid = oid self.oid = oid
self.array_oid = array_oid self.array_oid = array_oid
@ -857,17 +835,30 @@ class CompositeCaster(object):
"expecting %d components for the type %s, %d found instead" % "expecting %d components for the type %s, %d found instead" %
(len(self.atttypes), self.name, len(tokens))) (len(self.atttypes), self.name, len(tokens)))
attrs = [ curs.cast(oid, token) values = [ curs.cast(oid, token)
for oid, token in zip(self.atttypes, tokens) ] for oid, token in zip(self.atttypes, tokens) ]
return self._ctor(*attrs)
_re_tokenize = regex.compile(r""" return self.make(values)
def make(self, values):
"""Return a new Python object representing the data being casted.
*values* is the list of attributes, already casted into their Python
representation.
You can subclass this method to :ref:`customize the composite cast
<custom-composite>`.
"""
return self._ctor(values)
_re_tokenize = _re.compile(r"""
\(? ([,)]) # an empty token, representing NULL \(? ([,)]) # an empty token, representing NULL
| \(? " ((?: [^"] | "")*) " [,)] # or a quoted string | \(? " ((?: [^"] | "")*) " [,)] # or a quoted string
| \(? ([^",)]+) [,)] # or an unquoted string | \(? ([^",)]+) [,)] # or an unquoted string
""", regex.VERBOSE) """, _re.VERBOSE)
_re_undouble = regex.compile(r'(["\\])\1') _re_undouble = _re.compile(r'(["\\])\1')
@classmethod @classmethod
def tokenize(self, s): def tokenize(self, s):
@ -889,10 +880,10 @@ class CompositeCaster(object):
from collections import namedtuple from collections import namedtuple
except ImportError: except ImportError:
self.type = tuple self.type = tuple
self._ctor = lambda *args: tuple(args) self._ctor = self.type
else: else:
self.type = namedtuple(name, attnames) self.type = namedtuple(name, attnames)
self._ctor = self.type self._ctor = self.type._make
@classmethod @classmethod
def _from_db(self, name, conn_or_curs): def _from_db(self, name, conn_or_curs):
@ -941,10 +932,10 @@ ORDER BY attnum;
array_oid = recs[0][1] array_oid = recs[0][1]
type_attrs = [ (r[2], r[3]) for r in recs ] type_attrs = [ (r[2], r[3]) for r in recs ]
return CompositeCaster(tname, type_oid, type_attrs, return self(tname, type_oid, type_attrs,
array_oid=array_oid) array_oid=array_oid, schema=schema)
def register_composite(name, conn_or_curs, globally=False): def register_composite(name, conn_or_curs, globally=False, factory=None):
"""Register a typecaster to convert a composite type into a tuple. """Register a typecaster to convert a composite type into a tuple.
:param name: the name of a PostgreSQL composite type, e.g. created using :param name: the name of a PostgreSQL composite type, e.g. created using
@ -954,14 +945,15 @@ def register_composite(name, conn_or_curs, globally=False):
object, unless *globally* is set to `!True` object, unless *globally* is set to `!True`
:param globally: if `!False` (default) register the typecaster only on :param globally: if `!False` (default) register the typecaster only on
*conn_or_curs*, otherwise register it globally *conn_or_curs*, otherwise register it globally
:return: the registered `CompositeCaster` instance responsible for the :param factory: if specified it should be a `CompositeCaster` subclass: use
conversion it to :ref:`customize how to cast composite types <custom-composite>`
:return: the registered `CompositeCaster` or *factory* instance
.. versionchanged:: 2.4.3 responsible for the conversion
added support for array of composite types
""" """
caster = CompositeCaster._from_db(name, conn_or_curs) if factory is None:
factory = CompositeCaster
caster = factory._from_db(name, conn_or_curs)
_ext.register_type(caster.typecaster, not globally and conn_or_curs or None) _ext.register_type(caster.typecaster, not globally and conn_or_curs or None)
if caster.array_typecaster is not None: if caster.array_typecaster is not None:
@ -970,4 +962,11 @@ def register_composite(name, conn_or_curs, globally=False):
return caster return caster
__all__ = filter(lambda k: not k.startswith('_'), locals().keys()) # expose the json adaptation stuff into the module
from psycopg2._json import json, Json, register_json, register_default_json
# Expose range-related objects
from psycopg2._range import Range, NumericRange
from psycopg2._range import DateRange, DateTimeRange, DateTimeTZRange
from psycopg2._range import register_range, RangeAdapter, RangeCaster

View File

@ -27,30 +27,6 @@ This module implements thread-safe (and not) connection pools.
import psycopg2 import psycopg2
import psycopg2.extensions as _ext import psycopg2.extensions as _ext
try:
import logging
# create logger object for psycopg2 module and sub-modules
_logger = logging.getLogger("psycopg2")
def dbg(*args):
_logger.debug("psycopg2", ' '.join([str(x) for x in args]))
try:
import App # does this make sure that we're running in Zope?
_logger.info("installed. Logging using Python logging module")
except:
_logger.debug("installed. Logging using Python logging module")
except ImportError:
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(psycopg2.Error): class PoolError(psycopg2.Error):
pass pass

View File

@ -117,13 +117,6 @@ asis_setup(asisObject *self, PyObject *obj)
return 0; return 0;
} }
static int
asis_traverse(asisObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->wrapped);
return 0;
}
static void static void
asis_dealloc(PyObject* obj) asis_dealloc(PyObject* obj)
{ {
@ -156,12 +149,6 @@ asis_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
asis_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
asis_repr(asisObject *self) asis_repr(asisObject *self)
{ {
@ -177,63 +164,41 @@ asis_repr(asisObject *self)
PyTypeObject asisType = { PyTypeObject asisType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.AsIs", "psycopg2._psycopg.AsIs",
sizeof(asisObject), sizeof(asisObject), 0,
0,
asis_dealloc, /*tp_dealloc*/ asis_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)asis_repr, /*tp_repr*/ (reprfunc)asis_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)asis_str, /*tp_str*/ (reprfunc)asis_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
asisType_doc, /*tp_doc*/ asisType_doc, /*tp_doc*/
0, /*tp_traverse*/
(traverseproc)asis_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
asisObject_methods, /*tp_methods*/ asisObject_methods, /*tp_methods*/
asisObject_members, /*tp_members*/ asisObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
asis_init, /*tp_init*/ asis_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
asis_new, /*tp_new*/ asis_new, /*tp_new*/
(freefunc)asis_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
}; };

View File

@ -159,8 +159,7 @@ binary_prepare(binaryObject *self, PyObject *args)
self->conn = conn; self->conn = conn;
Py_INCREF(self->conn); Py_INCREF(self->conn);
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
static PyObject * static PyObject *
@ -221,17 +220,6 @@ binary_setup(binaryObject *self, PyObject *str)
return 0; return 0;
} }
static int
binary_traverse(PyObject *obj, visitproc visit, void *arg)
{
binaryObject *self = (binaryObject *)obj;
Py_VISIT(self->wrapped);
Py_VISIT(self->buffer);
Py_VISIT(self->conn);
return 0;
}
static void static void
binary_dealloc(PyObject* obj) binary_dealloc(PyObject* obj)
{ {
@ -266,12 +254,6 @@ binary_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
binary_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
binary_repr(binaryObject *self) binary_repr(binaryObject *self)
{ {
@ -286,61 +268,41 @@ binary_repr(binaryObject *self)
PyTypeObject binaryType = { PyTypeObject binaryType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Binary", "psycopg2._psycopg.Binary",
sizeof(binaryObject), sizeof(binaryObject), 0,
0,
binary_dealloc, /*tp_dealloc*/ binary_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)binary_repr, /*tp_repr*/ (reprfunc)binary_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)binary_str, /*tp_str*/ (reprfunc)binary_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
binaryType_doc, /*tp_doc*/ binaryType_doc, /*tp_doc*/
0, /*tp_traverse*/
binary_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
binaryObject_methods, /*tp_methods*/ binaryObject_methods, /*tp_methods*/
binaryObject_members, /*tp_members*/ binaryObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
binary_init, /*tp_init*/ binary_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
binary_new, /*tp_new*/ 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*/
}; };

View File

@ -183,15 +183,6 @@ pydatetime_setup(pydatetimeObject *self, PyObject *obj, int type)
return 0; return 0;
} }
static int
pydatetime_traverse(PyObject *obj, visitproc visit, void *arg)
{
pydatetimeObject *self = (pydatetimeObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pydatetime_dealloc(PyObject* obj) pydatetime_dealloc(PyObject* obj)
{ {
@ -223,12 +214,6 @@ pydatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pydatetime_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pydatetime_repr(pydatetimeObject *self) pydatetime_repr(pydatetimeObject *self)
{ {
@ -244,61 +229,41 @@ pydatetime_repr(pydatetimeObject *self)
PyTypeObject pydatetimeType = { PyTypeObject pydatetimeType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.datetime", "psycopg2._psycopg.datetime",
sizeof(pydatetimeObject), sizeof(pydatetimeObject), 0,
0,
pydatetime_dealloc, /*tp_dealloc*/ pydatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pydatetime_repr, /*tp_repr*/ (reprfunc)pydatetime_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pydatetime_str, /*tp_str*/ (reprfunc)pydatetime_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pydatetimeType_doc, /*tp_doc*/ pydatetimeType_doc, /*tp_doc*/
0, /*tp_traverse*/
pydatetime_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pydatetimeObject_methods, /*tp_methods*/ pydatetimeObject_methods, /*tp_methods*/
pydatetimeObject_members, /*tp_members*/ pydatetimeObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pydatetime_init, /*tp_init*/ pydatetime_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pydatetime_new, /*tp_new*/ 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*/
}; };

View File

@ -103,16 +103,11 @@ list_prepare(listObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn)) if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL; return NULL;
/* note that we don't copy the encoding from the connection, but take a
reference to it; we'll need it during the recursive adapt() call (the
encoding is here for a future expansion that will make .getquoted()
work even without a connection to the backend. */
Py_CLEAR(self->connection); Py_CLEAR(self->connection);
Py_INCREF(conn); Py_INCREF(conn);
self->connection = conn; self->connection = conn;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
static PyObject * static PyObject *
@ -154,7 +149,7 @@ static PyMethodDef listObject_methods[] = {
/* initialization and finalization methods */ /* initialization and finalization methods */
static int static int
list_setup(listObject *self, PyObject *obj, const char *enc) list_setup(listObject *self, PyObject *obj)
{ {
Dprintf("list_setup: init list object at %p, refcnt = " Dprintf("list_setup: init list object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
@ -164,9 +159,6 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
if (!PyList_Check(obj)) if (!PyList_Check(obj))
return -1; return -1;
/* FIXME: remove this orrible strdup */
if (enc) self->encoding = strdup(enc);
self->connection = NULL; self->connection = NULL;
Py_INCREF(obj); Py_INCREF(obj);
self->wrapped = obj; self->wrapped = obj;
@ -179,40 +171,42 @@ list_setup(listObject *self, PyObject *obj, const char *enc)
} }
static int static int
list_traverse(PyObject *obj, visitproc visit, void *arg) list_traverse(listObject *self, visitproc visit, void *arg)
{ {
listObject *self = (listObject *)obj;
Py_VISIT(self->wrapped); Py_VISIT(self->wrapped);
Py_VISIT(self->connection); Py_VISIT(self->connection);
return 0; return 0;
} }
static void static int
list_dealloc(PyObject* obj) list_clear(listObject *self)
{ {
listObject *self = (listObject *)obj;
Py_CLEAR(self->wrapped); Py_CLEAR(self->wrapped);
Py_CLEAR(self->connection); Py_CLEAR(self->connection);
if (self->encoding) free(self->encoding); return 0;
}
static void
list_dealloc(listObject* self)
{
PyObject_GC_UnTrack((PyObject *)self);
list_clear(self);
Dprintf("list_dealloc: deleted list object at %p, " Dprintf("list_dealloc: deleted list object at %p, "
"refcnt = " FORMAT_CODE_PY_SSIZE_T, obj, Py_REFCNT(obj)); "refcnt = " FORMAT_CODE_PY_SSIZE_T, self, Py_REFCNT(self));
Py_TYPE(obj)->tp_free(obj); Py_TYPE(self)->tp_free((PyObject *)self);
} }
static int static int
list_init(PyObject *obj, PyObject *args, PyObject *kwds) list_init(PyObject *obj, PyObject *args, PyObject *kwds)
{ {
PyObject *l; PyObject *l;
const char *enc = "latin-1"; /* default encoding as in Python */
if (!PyArg_ParseTuple(args, "O|s", &l, &enc)) if (!PyArg_ParseTuple(args, "O", &l))
return -1; return -1;
return list_setup((listObject *)obj, l, enc); return list_setup((listObject *)obj, l);
} }
static PyObject * static PyObject *
@ -221,12 +215,6 @@ list_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
list_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
list_repr(listObject *self) list_repr(listObject *self)
{ {
@ -241,61 +229,41 @@ list_repr(listObject *self)
PyTypeObject listType = { PyTypeObject listType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.List", "psycopg2._psycopg.List",
sizeof(listObject), sizeof(listObject), 0,
0, (destructor)list_dealloc, /*tp_dealloc*/
list_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)list_repr, /*tp_repr*/ (reprfunc)list_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)list_str, /*tp_str*/ (reprfunc)list_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
listType_doc, /*tp_doc*/ listType_doc, /*tp_doc*/
(traverseproc)list_traverse, /*tp_traverse*/
list_traverse, /*tp_traverse*/ (inquiry)list_clear, /*tp_clear*/
0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
listObject_methods, /*tp_methods*/ listObject_methods, /*tp_methods*/
listObject_members, /*tp_members*/ listObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
list_init, /*tp_init*/ list_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
list_new, /*tp_new*/ list_new, /*tp_new*/
(freefunc)list_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*/
}; };
@ -305,10 +273,9 @@ PyObject *
psyco_List(PyObject *module, PyObject *args) psyco_List(PyObject *module, PyObject *args)
{ {
PyObject *str; PyObject *str;
const char *enc = "latin-1"; /* default encoding as in Python */
if (!PyArg_ParseTuple(args, "O|s", &str, &enc)) if (!PyArg_ParseTuple(args, "O", &str))
return NULL; return NULL;
return PyObject_CallFunction((PyObject *)&listType, "Os", str, enc); return PyObject_CallFunctionObjArgs((PyObject *)&listType, "O", str, NULL);
} }

View File

@ -37,7 +37,6 @@ typedef struct {
PyObject *wrapped; PyObject *wrapped;
PyObject *connection; PyObject *connection;
char *encoding;
} listObject; } listObject;
HIDDEN PyObject *psyco_List(PyObject *module, PyObject *args); HIDDEN PyObject *psyco_List(PyObject *module, PyObject *args);

View File

@ -172,15 +172,6 @@ mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
return 0; return 0;
} }
static int
mxdatetime_traverse(PyObject *obj, visitproc visit, void *arg)
{
mxdatetimeObject *self = (mxdatetimeObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
mxdatetime_dealloc(PyObject* obj) mxdatetime_dealloc(PyObject* obj)
{ {
@ -214,12 +205,6 @@ mxdatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
mxdatetime_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
mxdatetime_repr(mxdatetimeObject *self) mxdatetime_repr(mxdatetimeObject *self)
{ {
@ -235,61 +220,41 @@ mxdatetime_repr(mxdatetimeObject *self)
PyTypeObject mxdatetimeType = { PyTypeObject mxdatetimeType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.MxDateTime", "psycopg2._psycopg.MxDateTime",
sizeof(mxdatetimeObject), sizeof(mxdatetimeObject), 0,
0,
mxdatetime_dealloc, /*tp_dealloc*/ mxdatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)mxdatetime_repr, /*tp_repr*/ (reprfunc)mxdatetime_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)mxdatetime_str, /*tp_str*/ (reprfunc)mxdatetime_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
mxdatetimeType_doc, /*tp_doc*/ mxdatetimeType_doc, /*tp_doc*/
0, /*tp_traverse*/
mxdatetime_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
mxdatetimeObject_methods, /*tp_methods*/ mxdatetimeObject_methods, /*tp_methods*/
mxdatetimeObject_members, /*tp_members*/ mxdatetimeObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
mxdatetime_init, /*tp_init*/ mxdatetime_init, /*tp_init*/
0, /*tp_alloc*/ 0, /*tp_alloc*/
mxdatetime_new, /*tp_new*/ 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*/
}; };

View File

@ -114,15 +114,6 @@ pboolean_setup(pbooleanObject *self, PyObject *obj)
return 0; return 0;
} }
static int
pboolean_traverse(PyObject *obj, visitproc visit, void *arg)
{
pbooleanObject *self = (pbooleanObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pboolean_dealloc(PyObject* obj) pboolean_dealloc(PyObject* obj)
{ {
@ -155,12 +146,6 @@ pboolean_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pboolean_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pboolean_repr(pbooleanObject *self) pboolean_repr(pbooleanObject *self)
{ {
@ -177,63 +162,41 @@ pboolean_repr(pbooleanObject *self)
PyTypeObject pbooleanType = { PyTypeObject pbooleanType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Boolean", "psycopg2._psycopg.Boolean",
sizeof(pbooleanObject), sizeof(pbooleanObject), 0,
0,
pboolean_dealloc, /*tp_dealloc*/ pboolean_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pboolean_repr, /*tp_repr*/ (reprfunc)pboolean_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pboolean_str, /*tp_str*/ (reprfunc)pboolean_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pbooleanType_doc, /*tp_doc*/ pbooleanType_doc, /*tp_doc*/
0, /*tp_traverse*/
pboolean_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pbooleanObject_methods, /*tp_methods*/ pbooleanObject_methods, /*tp_methods*/
pbooleanObject_members, /*tp_members*/ pbooleanObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pboolean_init, /*tp_init*/ pboolean_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pboolean_new, /*tp_new*/ 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*/
}; };

View File

@ -170,15 +170,6 @@ pdecimal_setup(pdecimalObject *self, PyObject *obj)
return 0; return 0;
} }
static int
pdecimal_traverse(PyObject *obj, visitproc visit, void *arg)
{
pdecimalObject *self = (pdecimalObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pdecimal_dealloc(PyObject* obj) pdecimal_dealloc(PyObject* obj)
{ {
@ -211,12 +202,6 @@ pdecimal_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pdecimal_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pdecimal_repr(pdecimalObject *self) pdecimal_repr(pdecimalObject *self)
{ {
@ -233,63 +218,41 @@ pdecimal_repr(pdecimalObject *self)
PyTypeObject pdecimalType = { PyTypeObject pdecimalType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Decimal", "psycopg2._psycopg.Decimal",
sizeof(pdecimalObject), sizeof(pdecimalObject), 0,
0,
pdecimal_dealloc, /*tp_dealloc*/ pdecimal_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pdecimal_repr, /*tp_repr*/ (reprfunc)pdecimal_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pdecimal_str, /*tp_str*/ (reprfunc)pdecimal_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pdecimalType_doc, /*tp_doc*/ pdecimalType_doc, /*tp_doc*/
0, /*tp_traverse*/
pdecimal_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pdecimalObject_methods, /*tp_methods*/ pdecimalObject_methods, /*tp_methods*/
pdecimalObject_members, /*tp_members*/ pdecimalObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pdecimal_init, /*tp_init*/ pdecimal_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pdecimal_new, /*tp_new*/ pdecimal_new, /*tp_new*/
(freefunc)pdecimal_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
}; };

View File

@ -143,15 +143,6 @@ pfloat_setup(pfloatObject *self, PyObject *obj)
return 0; return 0;
} }
static int
pfloat_traverse(PyObject *obj, visitproc visit, void *arg)
{
pfloatObject *self = (pfloatObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pfloat_dealloc(PyObject* obj) pfloat_dealloc(PyObject* obj)
{ {
@ -184,12 +175,6 @@ pfloat_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pfloat_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pfloat_repr(pfloatObject *self) pfloat_repr(pfloatObject *self)
{ {
@ -206,63 +191,41 @@ pfloat_repr(pfloatObject *self)
PyTypeObject pfloatType = { PyTypeObject pfloatType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Float", "psycopg2._psycopg.Float",
sizeof(pfloatObject), sizeof(pfloatObject), 0,
0,
pfloat_dealloc, /*tp_dealloc*/ pfloat_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pfloat_repr, /*tp_repr*/ (reprfunc)pfloat_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pfloat_str, /*tp_str*/ (reprfunc)pfloat_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pfloatType_doc, /*tp_doc*/ pfloatType_doc, /*tp_doc*/
0, /*tp_traverse*/
pfloat_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pfloatObject_methods, /*tp_methods*/ pfloatObject_methods, /*tp_methods*/
pfloatObject_members, /*tp_members*/ pfloatObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pfloat_init, /*tp_init*/ pfloat_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pfloat_new, /*tp_new*/ pfloat_new, /*tp_new*/
(freefunc)pfloat_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
}; };

View File

@ -129,15 +129,6 @@ pint_setup(pintObject *self, PyObject *obj)
return 0; return 0;
} }
static int
pint_traverse(PyObject *obj, visitproc visit, void *arg)
{
pintObject *self = (pintObject *)obj;
Py_VISIT(self->wrapped);
return 0;
}
static void static void
pint_dealloc(PyObject* obj) pint_dealloc(PyObject* obj)
{ {
@ -170,12 +161,6 @@ pint_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
pint_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
pint_repr(pintObject *self) pint_repr(pintObject *self)
{ {
@ -192,63 +177,41 @@ pint_repr(pintObject *self)
PyTypeObject pintType = { PyTypeObject pintType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Int", "psycopg2._psycopg.Int",
sizeof(pintObject), sizeof(pintObject), 0,
0,
pint_dealloc, /*tp_dealloc*/ pint_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)pint_repr, /*tp_repr*/ (reprfunc)pint_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)pint_str, /*tp_str*/ (reprfunc)pint_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
pintType_doc, /*tp_doc*/ pintType_doc, /*tp_doc*/
0, /*tp_traverse*/
pint_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
pintObject_methods, /*tp_methods*/ pintObject_methods, /*tp_methods*/
pintObject_members, /*tp_members*/ pintObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
pint_init, /*tp_init*/ pint_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
pint_new, /*tp_new*/ pint_new, /*tp_new*/
(freefunc)pint_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
}; };

View File

@ -32,26 +32,32 @@
#include <string.h> #include <string.h>
static const char *default_encoding = "latin1";
/* qstring_quote - do the quote process on plain and unicode strings */ /* qstring_quote - do the quote process on plain and unicode strings */
BORROWED static PyObject * static PyObject *
qstring_quote(qstringObject *self) qstring_quote(qstringObject *self)
{ {
PyObject *str; PyObject *str = NULL;
char *s, *buffer; char *s, *buffer = NULL;
Py_ssize_t len, qlen; Py_ssize_t len, qlen;
const char *encoding = default_encoding;
PyObject *rv = NULL;
/* if the wrapped object is an unicode object we can encode it to match /* 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 conn->encoding but if the encoding is not specified we don't know what
to do and we raise an exception */ to do and we raise an exception */
if (self->conn) {
encoding = self->conn->codec;
}
Dprintf("qstring_quote: encoding to %s", self->encoding); Dprintf("qstring_quote: encoding to %s", encoding);
if (PyUnicode_Check(self->wrapped) && self->encoding) { if (PyUnicode_Check(self->wrapped) && encoding) {
str = PyUnicode_AsEncodedString(self->wrapped, self->encoding, NULL); str = PyUnicode_AsEncodedString(self->wrapped, encoding, NULL);
Dprintf("qstring_quote: got encoded object at %p", str); Dprintf("qstring_quote: got encoded object at %p", str);
if (str == NULL) return NULL; if (str == NULL) goto exit;
} }
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
@ -68,30 +74,28 @@ qstring_quote(qstringObject *self)
else { else {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"can't quote non-string object (or missing encoding)"); "can't quote non-string object (or missing encoding)");
return NULL; goto exit;
} }
/* encode the string into buffer */ /* encode the string into buffer */
Bytes_AsStringAndSize(str, &s, &len); Bytes_AsStringAndSize(str, &s, &len);
if (!(buffer = psycopg_escape_string(self->conn, s, len, NULL, &qlen))) { if (!(buffer = psycopg_escape_string(self->conn, s, len, NULL, &qlen))) {
Py_DECREF(str); goto exit;
PyErr_NoMemory();
return NULL;
} }
if (qlen > (size_t) PY_SSIZE_T_MAX) { if (qlen > (size_t) PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_IndexError, PyErr_SetString(PyExc_IndexError,
"PG buffer too large to fit in Python buffer."); "PG buffer too large to fit in Python buffer.");
PyMem_Free(buffer); goto exit;
Py_DECREF(str);
return NULL;
} }
self->buffer = Bytes_FromStringAndSize(buffer, qlen); rv = Bytes_FromStringAndSize(buffer, qlen);
PyMem_Free(buffer);
Py_DECREF(str);
return self->buffer; exit:
PyMem_Free(buffer);
Py_XDECREF(str);
return rv;
} }
/* qstring_str, qstring_getquoted - return result of quoting */ /* qstring_str, qstring_getquoted - return result of quoting */
@ -100,7 +104,7 @@ static PyObject *
qstring_getquoted(qstringObject *self, PyObject *args) qstring_getquoted(qstringObject *self, PyObject *args)
{ {
if (self->buffer == NULL) { if (self->buffer == NULL) {
qstring_quote(self); self->buffer = qstring_quote(self);
} }
Py_XINCREF(self->buffer); Py_XINCREF(self->buffer);
return self->buffer; return self->buffer;
@ -115,25 +119,16 @@ qstring_str(qstringObject *self)
static PyObject * static PyObject *
qstring_prepare(qstringObject *self, PyObject *args) qstring_prepare(qstringObject *self, PyObject *args)
{ {
PyObject *conn; connectionObject *conn;
if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn)) if (!PyArg_ParseTuple(args, "O!", &connectionType, &conn))
return NULL; 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(((connectionObject *)conn)->codec);
Dprintf("qstring_prepare: set encoding to %s", self->encoding);
}
Py_CLEAR(self->conn); Py_CLEAR(self->conn);
Py_INCREF(conn); Py_INCREF(conn);
self->conn = conn; self->conn = conn;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
static PyObject * static PyObject *
@ -152,6 +147,18 @@ qstring_conform(qstringObject *self, PyObject *args)
return res; return res;
} }
static PyObject *
qstring_get_encoding(qstringObject *self)
{
const char *encoding = default_encoding;
if (self->conn) {
encoding = self->conn->codec;
}
return Text_FromUTF8(encoding);
}
/** the QuotedString object **/ /** the QuotedString object **/
/* object member list */ /* object member list */
@ -159,7 +166,6 @@ qstring_conform(qstringObject *self, PyObject *args)
static struct PyMemberDef qstringObject_members[] = { static struct PyMemberDef qstringObject_members[] = {
{"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY}, {"adapted", T_OBJECT, offsetof(qstringObject, wrapped), READONLY},
{"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY}, {"buffer", T_OBJECT, offsetof(qstringObject, buffer), READONLY},
{"encoding", T_STRING, offsetof(qstringObject, encoding), READONLY},
{NULL} {NULL}
}; };
@ -174,22 +180,24 @@ static PyMethodDef qstringObject_methods[] = {
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static PyGetSetDef qstringObject_getsets[] = {
{ "encoding",
(getter)qstring_get_encoding,
(setter)NULL,
"current encoding of the adapter" },
{NULL}
};
/* initialization and finalization methods */ /* initialization and finalization methods */
static int static int
qstring_setup(qstringObject *self, PyObject *str, const char *enc) qstring_setup(qstringObject *self, PyObject *str)
{ {
Dprintf("qstring_setup: init qstring object at %p, refcnt = " Dprintf("qstring_setup: init qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
self, Py_REFCNT(self) self, Py_REFCNT(self)
); );
self->buffer = NULL;
self->conn = NULL;
/* FIXME: remove this orrible strdup */
if (enc) self->encoding = strdup(enc);
Py_INCREF(str); Py_INCREF(str);
self->wrapped = str; self->wrapped = str;
@ -200,17 +208,6 @@ qstring_setup(qstringObject *self, PyObject *str, const char *enc)
return 0; return 0;
} }
static int
qstring_traverse(PyObject *obj, visitproc visit, void *arg)
{
qstringObject *self = (qstringObject *)obj;
Py_VISIT(self->wrapped);
Py_VISIT(self->buffer);
Py_VISIT(self->conn);
return 0;
}
static void static void
qstring_dealloc(PyObject* obj) qstring_dealloc(PyObject* obj)
{ {
@ -220,8 +217,6 @@ qstring_dealloc(PyObject* obj)
Py_CLEAR(self->buffer); Py_CLEAR(self->buffer);
Py_CLEAR(self->conn); Py_CLEAR(self->conn);
if (self->encoding) free(self->encoding);
Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = " Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
obj, Py_REFCNT(obj) obj, Py_REFCNT(obj)
@ -234,12 +229,11 @@ static int
qstring_init(PyObject *obj, PyObject *args, PyObject *kwds) qstring_init(PyObject *obj, PyObject *args, PyObject *kwds)
{ {
PyObject *str; PyObject *str;
const char *enc = "latin-1"; /* default encoding as in Python */
if (!PyArg_ParseTuple(args, "O|s", &str, &enc)) if (!PyArg_ParseTuple(args, "O", &str))
return -1; return -1;
return qstring_setup((qstringObject *)obj, str, enc); return qstring_setup((qstringObject *)obj, str);
} }
static PyObject * static PyObject *
@ -248,12 +242,6 @@ qstring_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
qstring_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
qstring_repr(qstringObject *self) qstring_repr(qstringObject *self)
{ {
@ -264,66 +252,46 @@ qstring_repr(qstringObject *self)
/* object type */ /* object type */
#define qstringType_doc \ #define qstringType_doc \
"QuotedString(str, enc) -> new quoted object with 'enc' encoding" "QuotedString(str) -> new quoted object"
PyTypeObject qstringType = { PyTypeObject qstringType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.QuotedString", "psycopg2._psycopg.QuotedString",
sizeof(qstringObject), sizeof(qstringObject), 0,
0,
qstring_dealloc, /*tp_dealloc*/ qstring_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)qstring_repr, /*tp_repr*/ (reprfunc)qstring_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)qstring_str, /*tp_str*/ (reprfunc)qstring_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
qstringType_doc, /*tp_doc*/ qstringType_doc, /*tp_doc*/
0, /*tp_traverse*/
qstring_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
qstringObject_methods, /*tp_methods*/ qstringObject_methods, /*tp_methods*/
qstringObject_members, /*tp_members*/ qstringObject_members, /*tp_members*/
0, /*tp_getset*/ qstringObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
qstring_init, /*tp_init*/ qstring_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
qstring_new, /*tp_new*/ 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*/
}; };
@ -333,10 +301,9 @@ PyObject *
psyco_QuotedString(PyObject *module, PyObject *args) psyco_QuotedString(PyObject *module, PyObject *args)
{ {
PyObject *str; PyObject *str;
const char *enc = "latin-1"; /* default encoding as in Python */
if (!PyArg_ParseTuple(args, "O|s", &str, &enc)) if (!PyArg_ParseTuple(args, "O", &str))
return NULL; return NULL;
return PyObject_CallFunction((PyObject *)&qstringType, "Os", str, enc); return PyObject_CallFunctionObjArgs((PyObject *)&qstringType, str, NULL);
} }

View File

@ -37,13 +37,8 @@ typedef struct {
PyObject *wrapped; PyObject *wrapped;
PyObject *buffer; PyObject *buffer;
/* NOTE: this used to be a PostgreSQL encoding: changed in 2.3.2 to be a
* Python codec name. I don't expect there has been any user for this
* object other than adapting str/unicode, so I don't expect client code
* broken for this reason. */
char *encoding;
PyObject *conn; connectionObject *conn;
} qstringObject; } qstringObject;
/* functions exported to psycopgmodule.c */ /* functions exported to psycopgmodule.c */

View File

@ -74,7 +74,8 @@ struct connectionObject_notice {
const char *message; const char *message;
}; };
typedef struct { /* the typedef is forward-declared in psycopg.h */
struct connectionObject {
PyObject_HEAD PyObject_HEAD
pthread_mutex_t lock; /* the global connection lock */ pthread_mutex_t lock; /* the global connection lock */
@ -88,7 +89,7 @@ typedef struct {
2 that something horrible happened */ 2 that something horrible happened */
long int mark; /* number of commits/rollbacks done so far */ long int mark; /* number of commits/rollbacks done so far */
int status; /* status of the connection */ int status; /* status of the connection */
XidObject *tpc_xid; /* Transaction ID in two-phase commit */ xidObject *tpc_xid; /* Transaction ID in two-phase commit */
long int async; /* 1 means the connection is async */ long int async; /* 1 means the connection is async */
int protocol; /* protocol version */ int protocol; /* protocol version */
@ -120,7 +121,8 @@ typedef struct {
int autocommit; int autocommit;
} connectionObject; PyObject *cursor_factory; /* default cursor factory from cursor() */
};
/* map isolation level values into a numeric const */ /* map isolation level values into a numeric const */
typedef struct { typedef struct {
@ -134,7 +136,6 @@ HIDDEN int conn_get_standard_conforming_strings(PGconn *pgconn);
RAISES_NEG HIDDEN int conn_get_isolation_level(connectionObject *self); RAISES_NEG HIDDEN int conn_get_isolation_level(connectionObject *self);
HIDDEN int conn_get_protocol_version(PGconn *pgconn); HIDDEN int conn_get_protocol_version(PGconn *pgconn);
HIDDEN int conn_get_server_version(PGconn *pgconn); HIDDEN int conn_get_server_version(PGconn *pgconn);
HIDDEN PGcancel *conn_get_cancel(PGconn *pgconn);
HIDDEN void conn_notice_process(connectionObject *self); HIDDEN void conn_notice_process(connectionObject *self);
HIDDEN void conn_notice_clean(connectionObject *self); HIDDEN void conn_notice_clean(connectionObject *self);
HIDDEN void conn_notifies_process(connectionObject *self); HIDDEN void conn_notifies_process(connectionObject *self);
@ -151,9 +152,9 @@ HIDDEN int conn_set_autocommit(connectionObject *self, int value);
RAISES_NEG HIDDEN int conn_switch_isolation_level(connectionObject *self, int level); RAISES_NEG HIDDEN int conn_switch_isolation_level(connectionObject *self, int level);
RAISES_NEG HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc); RAISES_NEG HIDDEN int conn_set_client_encoding(connectionObject *self, const char *enc);
HIDDEN int conn_poll(connectionObject *self); HIDDEN int conn_poll(connectionObject *self);
RAISES_NEG HIDDEN int conn_tpc_begin(connectionObject *self, XidObject *xid); RAISES_NEG HIDDEN int conn_tpc_begin(connectionObject *self, xidObject *xid);
RAISES_NEG HIDDEN int conn_tpc_command(connectionObject *self, RAISES_NEG HIDDEN int conn_tpc_command(connectionObject *self,
const char *cmd, XidObject *xid); const char *cmd, xidObject *xid);
HIDDEN PyObject *conn_tpc_recover(connectionObject *self); HIDDEN PyObject *conn_tpc_recover(connectionObject *self);
/* exception-raising macros */ /* exception-raising macros */

View File

@ -183,7 +183,7 @@ conn_notifies_process(connectionObject *self)
if (!(channel = conn_text_from_chars(self, pgn->relname))) { goto error; } if (!(channel = conn_text_from_chars(self, pgn->relname))) { goto error; }
if (!(payload = conn_text_from_chars(self, pgn->extra))) { goto error; } if (!(payload = conn_text_from_chars(self, pgn->extra))) { goto error; }
if (!(notify = PyObject_CallFunctionObjArgs((PyObject *)&NotifyType, if (!(notify = PyObject_CallFunctionObjArgs((PyObject *)&notifyType,
pid, channel, payload, NULL))) { pid, channel, payload, NULL))) {
goto error; goto error;
} }
@ -439,10 +439,22 @@ conn_get_server_version(PGconn *pgconn)
return (int)PQserverVersion(pgconn); return (int)PQserverVersion(pgconn);
} }
PGcancel * /* set up the cancel key of the connection.
conn_get_cancel(PGconn *pgconn) * On success return 0, else set an exception and return -1
*/
RAISES_NEG static int
conn_setup_cancel(connectionObject *self, PGconn *pgconn)
{ {
return PQgetCancel(pgconn); if (self->cancel) {
PQfreeCancel(self->cancel);
}
if (!(self->cancel = PQgetCancel(self->pgconn))) {
PyErr_SetString(OperationalError, "can't get cancellation key");
return -1;
}
return 0;
} }
@ -486,9 +498,7 @@ conn_setup(connectionObject *self, PGconn *pgconn)
return -1; return -1;
} }
self->cancel = conn_get_cancel(self->pgconn); if (0 > conn_setup_cancel(self, pgconn)) {
if (self->cancel == NULL) {
PyErr_SetString(OperationalError, "can't get cancellation key");
return -1; return -1;
} }
@ -788,10 +798,8 @@ _conn_poll_setup_async(connectionObject *self)
if (0 > conn_read_encoding(self, self->pgconn)) { if (0 > conn_read_encoding(self, self->pgconn)) {
break; break;
} }
self->cancel = conn_get_cancel(self->pgconn); if (0 > conn_setup_cancel(self, self->pgconn)) {
if (self->cancel == NULL) { return -1;
PyErr_SetString(OperationalError, "can't get cancellation key");
break;
} }
/* asynchronous connections always use isolation level 0, the user is /* asynchronous connections always use isolation level 0, the user is
@ -890,7 +898,7 @@ conn_poll(connectionObject *self)
} }
curs = (cursorObject *)py_curs; curs = (cursorObject *)py_curs;
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
curs->pgres = pq_get_last_result(self); curs->pgres = pq_get_last_result(self);
/* fetch the tuples (if there are any) and build the result. We /* fetch the tuples (if there are any) and build the result. We
@ -918,7 +926,8 @@ conn_poll(connectionObject *self)
void void
conn_close(connectionObject *self) conn_close(connectionObject *self)
{ {
if (self->closed) { /* a connection with closed == 2 still requires cleanup */
if (self->closed == 1) {
return; return;
} }
@ -936,7 +945,7 @@ conn_close(connectionObject *self)
void conn_close_locked(connectionObject *self) void conn_close_locked(connectionObject *self)
{ {
if (self->closed) { if (self->closed == 1) {
return; return;
} }
@ -957,8 +966,6 @@ void conn_close_locked(connectionObject *self)
PQfinish(self->pgconn); PQfinish(self->pgconn);
self->pgconn = NULL; self->pgconn = NULL;
Dprintf("conn_close: PQfinish called"); Dprintf("conn_close: PQfinish called");
PQfreeCancel(self->cancel);
self->cancel = NULL;
} }
} }
@ -1213,7 +1220,7 @@ exit:
* until PREPARE. */ * until PREPARE. */
RAISES_NEG int RAISES_NEG int
conn_tpc_begin(connectionObject *self, XidObject *xid) conn_tpc_begin(connectionObject *self, xidObject *xid)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
char *error = NULL; char *error = NULL;
@ -1247,7 +1254,7 @@ conn_tpc_begin(connectionObject *self, XidObject *xid)
* for many commands and for recovered transactions. */ * for many commands and for recovered transactions. */
RAISES_NEG int RAISES_NEG int
conn_tpc_command(connectionObject *self, const char *cmd, XidObject *xid) conn_tpc_command(connectionObject *self, const char *cmd, xidObject *xid)
{ {
PGresult *pgres = NULL; PGresult *pgres = NULL;
char *error = NULL; char *error = NULL;

View File

@ -52,63 +52,74 @@
static PyObject * static PyObject *
psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *kwargs) psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *kwargs)
{ {
PyObject *obj; PyObject *obj = NULL;
PyObject *rv = NULL;
PyObject *name = Py_None; PyObject *name = Py_None;
PyObject *factory = (PyObject *)&cursorType; PyObject *factory = (PyObject *)&cursorType;
PyObject *withhold = Py_False; PyObject *withhold = Py_False;
PyObject *scrollable = Py_None;
static char *kwlist[] = {"name", "cursor_factory", "withhold", NULL}; static char *kwlist[] = {
"name", "cursor_factory", "withhold", "scrollable", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO", kwlist,
&name, &factory, &withhold)) {
return NULL;
}
if (PyObject_IsTrue(withhold) && (name == Py_None)) {
PyErr_SetString(ProgrammingError,
"'withhold=True can be specified only for named cursors");
return NULL;
}
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
if (self->cursor_factory && self->cursor_factory != Py_None) {
factory = self->cursor_factory;
}
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "|OOOO", kwlist,
&name, &factory, &withhold, &scrollable)) {
goto exit;
}
if (self->status != CONN_STATUS_READY && if (self->status != CONN_STATUS_READY &&
self->status != CONN_STATUS_BEGIN && self->status != CONN_STATUS_BEGIN &&
self->status != CONN_STATUS_PREPARED) { self->status != CONN_STATUS_PREPARED) {
PyErr_SetString(OperationalError, PyErr_SetString(OperationalError,
"asynchronous connection attempt underway"); "asynchronous connection attempt underway");
return NULL; goto exit;
} }
if (name != Py_None && self->async == 1) { if (name != Py_None && self->async == 1) {
PyErr_SetString(ProgrammingError, PyErr_SetString(ProgrammingError,
"asynchronous connections " "asynchronous connections "
"cannot produce named cursors"); "cannot produce named cursors");
return NULL; goto exit;
} }
Dprintf("psyco_conn_cursor: new %s cursor for connection at %p", Dprintf("psyco_conn_cursor: new %s cursor for connection at %p",
(name == Py_None ? "unnamed" : "named"), self); (name == Py_None ? "unnamed" : "named"), self);
if (!(obj = PyObject_CallFunctionObjArgs(factory, self, name, NULL))) { if (!(obj = PyObject_CallFunctionObjArgs(factory, self, name, NULL))) {
return NULL; goto exit;
} }
if (PyObject_IsInstance(obj, (PyObject *)&cursorType) == 0) { if (PyObject_IsInstance(obj, (PyObject *)&cursorType) == 0) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"cursor factory must be subclass of psycopg2._psycopg.cursor"); "cursor factory must be subclass of psycopg2._psycopg.cursor");
Py_DECREF(obj); goto exit;
return NULL;
} }
if (PyObject_IsTrue(withhold)) if (0 != psyco_curs_withhold_set((cursorObject *)obj, withhold)) {
((cursorObject*)obj)->withhold = 1; goto exit;
}
if (0 != psyco_curs_scrollable_set((cursorObject *)obj, scrollable)) {
goto exit;
}
Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = " Dprintf("psyco_conn_cursor: new cursor at %p: refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
obj, Py_REFCNT(obj) obj, Py_REFCNT(obj)
); );
return obj;
rv = obj;
obj = NULL;
exit:
Py_XDECREF(obj);
return rv;
} }
@ -117,14 +128,13 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *kwargs)
#define psyco_conn_close_doc "close() -- Close the connection." #define psyco_conn_close_doc "close() -- Close the connection."
static PyObject * static PyObject *
psyco_conn_close(connectionObject *self, PyObject *args) psyco_conn_close(connectionObject *self)
{ {
Dprintf("psyco_conn_close: closing connection at %p", self); Dprintf("psyco_conn_close: closing connection at %p", self);
conn_close(self); conn_close(self);
Dprintf("psyco_conn_close: connection at %p closed", self); Dprintf("psyco_conn_close: connection at %p closed", self);
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -133,7 +143,7 @@ psyco_conn_close(connectionObject *self, PyObject *args)
#define psyco_conn_commit_doc "commit() -- Commit all changes to database." #define psyco_conn_commit_doc "commit() -- Commit all changes to database."
static PyObject * static PyObject *
psyco_conn_commit(connectionObject *self, PyObject *args) psyco_conn_commit(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, commit); EXC_IF_CONN_ASYNC(self, commit);
@ -142,8 +152,7 @@ psyco_conn_commit(connectionObject *self, PyObject *args)
if (conn_commit(self) < 0) if (conn_commit(self) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -153,7 +162,7 @@ psyco_conn_commit(connectionObject *self, PyObject *args)
"rollback() -- Roll back all changes done to database." "rollback() -- Roll back all changes done to database."
static PyObject * static PyObject *
psyco_conn_rollback(connectionObject *self, PyObject *args) psyco_conn_rollback(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, rollback); EXC_IF_CONN_ASYNC(self, rollback);
@ -162,8 +171,7 @@ psyco_conn_rollback(connectionObject *self, PyObject *args)
if (conn_rollback(self) < 0) if (conn_rollback(self) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -176,7 +184,7 @@ psyco_conn_xid(connectionObject *self, PyObject *args, PyObject *kwargs)
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_TPC_NOT_SUPPORTED(self); EXC_IF_TPC_NOT_SUPPORTED(self);
return PyObject_Call((PyObject *)&XidType, args, kwargs); return PyObject_Call((PyObject *)&xidType, args, kwargs);
} }
@ -187,7 +195,7 @@ static PyObject *
psyco_conn_tpc_begin(connectionObject *self, PyObject *args) psyco_conn_tpc_begin(connectionObject *self, PyObject *args)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
XidObject *xid = NULL; xidObject *xid = NULL;
PyObject *oxid; PyObject *oxid;
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
@ -227,7 +235,7 @@ exit:
"tpc_prepare() -- perform the first phase of a two-phase transaction." "tpc_prepare() -- perform the first phase of a two-phase transaction."
static PyObject * static PyObject *
psyco_conn_tpc_prepare(connectionObject *self, PyObject *args) psyco_conn_tpc_prepare(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, tpc_prepare); EXC_IF_CONN_ASYNC(self, tpc_prepare);
@ -247,8 +255,7 @@ psyco_conn_tpc_prepare(connectionObject *self, PyObject *args)
* can be performed until commit. */ * can be performed until commit. */
self->status = CONN_STATUS_PREPARED; self->status = CONN_STATUS_PREPARED;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -279,7 +286,7 @@ _psyco_conn_tpc_finish(connectionObject *self, PyObject *args,
_finish_f opc_f, char *tpc_cmd) _finish_f opc_f, char *tpc_cmd)
{ {
PyObject *oxid = NULL; PyObject *oxid = NULL;
XidObject *xid = NULL; xidObject *xid = NULL;
PyObject *rv = NULL; PyObject *rv = NULL;
if (!PyArg_ParseTuple(args, "|O", &oxid)) { goto exit; } if (!PyArg_ParseTuple(args, "|O", &oxid)) { goto exit; }
@ -371,7 +378,7 @@ psyco_conn_tpc_rollback(connectionObject *self, PyObject *args)
"tpc_recover() -- returns a list of pending transaction IDs." "tpc_recover() -- returns a list of pending transaction IDs."
static PyObject * static PyObject *
psyco_conn_tpc_recover(connectionObject *self, PyObject *args) psyco_conn_tpc_recover(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
EXC_IF_CONN_ASYNC(self, tpc_recover); EXC_IF_CONN_ASYNC(self, tpc_recover);
@ -382,6 +389,54 @@ psyco_conn_tpc_recover(connectionObject *self, PyObject *args)
} }
#define psyco_conn_enter_doc \
"__enter__ -> self"
static PyObject *
psyco_conn_enter(connectionObject *self)
{
EXC_IF_CONN_CLOSED(self);
Py_INCREF(self);
return (PyObject *)self;
}
#define psyco_conn_exit_doc \
"__exit__ -- commit if no exception, else roll back"
static PyObject *
psyco_conn_exit(connectionObject *self, PyObject *args)
{
PyObject *type, *name, *tb;
PyObject *tmp = NULL;
PyObject *rv = NULL;
if (!PyArg_ParseTuple(args, "OOO", &type, &name, &tb)) {
goto exit;
}
if (type == Py_None) {
if (!(tmp = PyObject_CallMethod((PyObject *)self, "commit", NULL))) {
goto exit;
}
} else {
if (!(tmp = PyObject_CallMethod((PyObject *)self, "rollback", NULL))) {
goto exit;
}
}
/* success (of the commit or rollback, there may have been an exception in
* the block). Return None to avoid swallowing the exception */
rv = Py_None;
Py_INCREF(rv);
exit:
Py_XDECREF(tmp);
return rv;
}
#ifdef PSYCOPG_EXTENSIONS #ifdef PSYCOPG_EXTENSIONS
@ -528,8 +583,7 @@ psyco_conn_set_session(connectionObject *self, PyObject *args, PyObject *kwargs)
return NULL; return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -611,8 +665,7 @@ psyco_conn_set_isolation_level(connectionObject *self, PyObject *args)
return NULL; return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* set_client_encoding method - set client encoding */ /* set_client_encoding method - set client encoding */
@ -645,7 +698,7 @@ psyco_conn_set_client_encoding(connectionObject *self, PyObject *args)
"get_transaction_status() -- Get backend transaction status." "get_transaction_status() -- Get backend transaction status."
static PyObject * static PyObject *
psyco_conn_get_transaction_status(connectionObject *self, PyObject *args) psyco_conn_get_transaction_status(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
@ -675,8 +728,7 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
val = PQparameterStatus(self->pgconn, param); val = PQparameterStatus(self->pgconn, param);
if (!val) { if (!val) {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
return conn_text_from_chars(self, val); return conn_text_from_chars(self, val);
} }
@ -749,7 +801,7 @@ psyco_conn_lobject(connectionObject *self, PyObject *args, PyObject *keywds)
"get_backend_pid() -- Get backend process id." "get_backend_pid() -- Get backend process id."
static PyObject * static PyObject *
psyco_conn_get_backend_pid(connectionObject *self, PyObject *args) psyco_conn_get_backend_pid(connectionObject *self)
{ {
EXC_IF_CONN_CLOSED(self); EXC_IF_CONN_CLOSED(self);
@ -762,7 +814,7 @@ psyco_conn_get_backend_pid(connectionObject *self, PyObject *args)
"reset() -- Reset current connection to defaults." "reset() -- Reset current connection to defaults."
static PyObject * static PyObject *
psyco_conn_reset(connectionObject *self, PyObject *args) psyco_conn_reset(connectionObject *self)
{ {
int res; int res;
@ -776,8 +828,7 @@ psyco_conn_reset(connectionObject *self, PyObject *args)
if (res < 0) if (res < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
static PyObject * static PyObject *
@ -790,7 +841,7 @@ psyco_conn_get_exception(PyObject *self, void *closure)
} }
static PyObject * static PyObject *
psyco_conn_poll(connectionObject *self, PyObject *args) psyco_conn_poll(connectionObject *self)
{ {
int res; int res;
@ -812,7 +863,7 @@ psyco_conn_poll(connectionObject *self, PyObject *args)
"fileno() -> int -- Return file descriptor associated to database connection." "fileno() -> int -- Return file descriptor associated to database connection."
static PyObject * static PyObject *
psyco_conn_fileno(connectionObject *self, PyObject *args) psyco_conn_fileno(connectionObject *self)
{ {
long int socket; long int socket;
@ -831,7 +882,7 @@ psyco_conn_fileno(connectionObject *self, PyObject *args)
"executing an asynchronous operation." "executing an asynchronous operation."
static PyObject * static PyObject *
psyco_conn_isexecuting(connectionObject *self, PyObject *args) psyco_conn_isexecuting(connectionObject *self)
{ {
/* synchronous connections will always return False */ /* synchronous connections will always return False */
if (self->async == 0) { if (self->async == 0) {
@ -863,7 +914,7 @@ psyco_conn_isexecuting(connectionObject *self, PyObject *args)
"cancel() -- cancel the current operation" "cancel() -- cancel the current operation"
static PyObject * static PyObject *
psyco_conn_cancel(connectionObject *self, PyObject *args) psyco_conn_cancel(connectionObject *self)
{ {
char errbuf[256]; char errbuf[256];
@ -884,8 +935,7 @@ psyco_conn_cancel(connectionObject *self, PyObject *args)
PyErr_SetString(OperationalError, errbuf); PyErr_SetString(OperationalError, errbuf);
return NULL; return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
#endif /* PSYCOPG_EXTENSIONS */ #endif /* PSYCOPG_EXTENSIONS */
@ -917,6 +967,10 @@ static struct PyMethodDef connectionObject_methods[] = {
METH_VARARGS, psyco_conn_tpc_rollback_doc}, METH_VARARGS, psyco_conn_tpc_rollback_doc},
{"tpc_recover", (PyCFunction)psyco_conn_tpc_recover, {"tpc_recover", (PyCFunction)psyco_conn_tpc_recover,
METH_NOARGS, psyco_conn_tpc_recover_doc}, METH_NOARGS, psyco_conn_tpc_recover_doc},
{"__enter__", (PyCFunction)psyco_conn_enter,
METH_NOARGS, psyco_conn_enter_doc},
{"__exit__", (PyCFunction)psyco_conn_exit,
METH_VARARGS, psyco_conn_exit_doc},
#ifdef PSYCOPG_EXTENSIONS #ifdef PSYCOPG_EXTENSIONS
{"set_session", (PyCFunction)psyco_conn_set_session, {"set_session", (PyCFunction)psyco_conn_set_session,
METH_VARARGS|METH_KEYWORDS, psyco_conn_set_session_doc}, METH_VARARGS|METH_KEYWORDS, psyco_conn_set_session_doc},
@ -963,6 +1017,8 @@ static struct PyMemberDef connectionObject_members[] = {
{"status", T_INT, {"status", T_INT,
offsetof(connectionObject, status), READONLY, offsetof(connectionObject, status), READONLY,
"The current transaction status."}, "The current transaction status."},
{"cursor_factory", T_OBJECT, offsetof(connectionObject, cursor_factory), 0,
"Default cursor_factory for cursor()."},
{"string_types", T_OBJECT, offsetof(connectionObject, string_types), READONLY, {"string_types", T_OBJECT, offsetof(connectionObject, string_types), READONLY,
"A set of typecasters to convert textual values."}, "A set of typecasters to convert textual values."},
{"binary_types", T_OBJECT, offsetof(connectionObject, binary_types), READONLY, {"binary_types", T_OBJECT, offsetof(connectionObject, binary_types), READONLY,
@ -1019,10 +1075,7 @@ connection_setup(connectionObject *self, const char *dsn, long int async)
self, async, Py_REFCNT(self) self, async, Py_REFCNT(self)
); );
if (!(self->dsn = strdup(dsn))) { if (0 > psycopg_strdup(&self->dsn, dsn, 0)) { goto exit; }
PyErr_NoMemory();
goto exit;
}
if (!(self->notice_list = PyList_New(0))) { goto exit; } if (!(self->notice_list = PyList_New(0))) { goto exit; }
if (!(self->notifies = PyList_New(0))) { goto exit; } if (!(self->notifies = PyList_New(0))) { goto exit; }
self->async = async; self->async = async;
@ -1057,26 +1110,9 @@ exit:
return res; return res;
} }
static void static int
connection_dealloc(PyObject* obj) connection_clear(connectionObject *self)
{ {
connectionObject *self = (connectionObject *)obj;
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
PyObject_GC_UnTrack(self);
if (self->closed == 0) conn_close(self);
conn_notice_clean(self);
if (self->dsn) free(self->dsn);
PyMem_Free(self->encoding);
PyMem_Free(self->codec);
if (self->critical) free(self->critical);
Py_CLEAR(self->tpc_xid); Py_CLEAR(self->tpc_xid);
Py_CLEAR(self->async_cursor); Py_CLEAR(self->async_cursor);
Py_CLEAR(self->notice_list); Py_CLEAR(self->notice_list);
@ -1084,6 +1120,31 @@ connection_dealloc(PyObject* obj)
Py_CLEAR(self->notifies); Py_CLEAR(self->notifies);
Py_CLEAR(self->string_types); Py_CLEAR(self->string_types);
Py_CLEAR(self->binary_types); Py_CLEAR(self->binary_types);
return 0;
}
static void
connection_dealloc(PyObject* obj)
{
connectionObject *self = (connectionObject *)obj;
conn_close(self);
PyObject_GC_UnTrack(self);
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
conn_notice_clean(self);
PyMem_Free(self->dsn);
PyMem_Free(self->encoding);
PyMem_Free(self->codec);
if (self->critical) free(self->critical);
if (self->cancel) PQfreeCancel(self->cancel);
connection_clear(self);
pthread_mutex_destroy(&(self->lock)); pthread_mutex_destroy(&(self->lock));
@ -1114,12 +1175,6 @@ connection_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
connection_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
connection_repr(connectionObject *self) connection_repr(connectionObject *self)
{ {
@ -1154,8 +1209,7 @@ connection_traverse(connectionObject *self, visitproc visit, void *arg)
PyTypeObject connectionType = { PyTypeObject connectionType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.connection", "psycopg2._psycopg.connection",
sizeof(connectionObject), sizeof(connectionObject), 0,
0,
connection_dealloc, /*tp_dealloc*/ connection_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
@ -1166,47 +1220,30 @@ PyTypeObject connectionType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)connection_repr, /*tp_str*/ (reprfunc)connection_repr, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_HAVE_WEAKREFS, Py_TPFLAGS_HAVE_WEAKREFS,
/*tp_flags*/ /*tp_flags*/
connectionType_doc, /*tp_doc*/ connectionType_doc, /*tp_doc*/
(traverseproc)connection_traverse, /*tp_traverse*/ (traverseproc)connection_traverse, /*tp_traverse*/
0, /*tp_clear*/ (inquiry)connection_clear, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
offsetof(connectionObject, weakreflist), /* tp_weaklistoffset */ offsetof(connectionObject, weakreflist), /* tp_weaklistoffset */
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
connectionObject_methods, /*tp_methods*/ connectionObject_methods, /*tp_methods*/
connectionObject_members, /*tp_members*/ connectionObject_members, /*tp_members*/
connectionObject_getsets, /*tp_getset*/ connectionObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
connection_init, /*tp_init*/ connection_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
connection_new, /*tp_new*/ 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*/
}; };

View File

@ -44,6 +44,11 @@ struct cursorObject {
int notuples:1; /* 1 if the command was not a SELECT query */ int notuples:1; /* 1 if the command was not a SELECT query */
int withhold:1; /* 1 if the cursor is named and uses WITH HOLD */ int withhold:1; /* 1 if the cursor is named and uses WITH HOLD */
int scrollable; /* 1 if the cursor is named and SCROLLABLE,
0 if not scrollable
-1 if undefined (PG may decide scrollable or not)
*/
long int rowcount; /* number of rows affected by last execute */ long int rowcount; /* number of rows affected by last execute */
long int columns; /* number of columns fetched from the db */ long int columns; /* number of columns fetched from the db */
long int arraysize; /* how many rows should fetchmany() return */ long int arraysize; /* how many rows should fetchmany() return */
@ -84,9 +89,11 @@ struct cursorObject {
}; };
/* C-callable functions in cursor_int.c and cursor_ext.c */ /* C-callable functions in cursor_int.c and cursor_type.c */
BORROWED HIDDEN PyObject *curs_get_cast(cursorObject *self, PyObject *oid); BORROWED HIDDEN PyObject *curs_get_cast(cursorObject *self, PyObject *oid);
HIDDEN void curs_reset(cursorObject *self); HIDDEN void curs_reset(cursorObject *self);
HIDDEN int psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue);
HIDDEN int psyco_curs_scrollable_set(cursorObject *self, PyObject *pyvalue);
/* exception-raising macros */ /* exception-raising macros */
#define EXC_IF_CURS_CLOSED(self) \ #define EXC_IF_CURS_CLOSED(self) \

View File

@ -72,19 +72,11 @@ curs_get_cast(cursorObject *self, PyObject *oid)
void void
curs_reset(cursorObject *self) curs_reset(cursorObject *self)
{ {
PyObject *tmp;
/* initialize some variables to default values */ /* initialize some variables to default values */
self->notuples = 1; self->notuples = 1;
self->rowcount = -1; self->rowcount = -1;
self->row = 0; self->row = 0;
tmp = self->description; Py_CLEAR(self->description);
Py_INCREF(Py_None); Py_CLEAR(self->casts);
self->description = Py_None;
Py_XDECREF(tmp);
tmp = self->casts;
self->casts = NULL;
Py_XDECREF(tmp);
} }

View File

@ -50,7 +50,7 @@ extern PyObject *pyPsycopgTzFixedOffsetTimezone;
"close() -- Close the cursor." "close() -- Close the cursor."
static PyObject * static PyObject *
psyco_curs_close(cursorObject *self, PyObject *args) psyco_curs_close(cursorObject *self)
{ {
EXC_IF_ASYNC_IN_PROGRESS(self, close); EXC_IF_ASYNC_IN_PROGRESS(self, close);
@ -70,8 +70,7 @@ psyco_curs_close(cursorObject *self, PyObject *args)
Dprintf("psyco_curs_close: cursor at %p closed", self); Dprintf("psyco_curs_close: cursor at %p closed", self);
exit: exit:
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -118,7 +117,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
if (kind == 2) { if (kind == 2) {
Py_XDECREF(n); Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs, psyco_set_error(ProgrammingError, curs,
"argument formats can't be mixed", NULL, NULL); "argument formats can't be mixed");
return -1; return -1;
} }
kind = 1; kind = 1;
@ -190,7 +189,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
/* we found %( but not a ) */ /* we found %( but not a ) */
Py_XDECREF(n); Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs, psyco_set_error(ProgrammingError, curs,
"incomplete placeholder: '%(' without ')'", NULL, NULL); "incomplete placeholder: '%(' without ')'");
return -1; return -1;
} }
c = d + 1; /* after the ) */ c = d + 1; /* after the ) */
@ -205,7 +204,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
if (kind == 1) { if (kind == 1) {
Py_XDECREF(n); Py_XDECREF(n);
psyco_set_error(ProgrammingError, curs, psyco_set_error(ProgrammingError, curs,
"argument formats can't be mixed", NULL, NULL); "argument formats can't be mixed");
return -1; return -1;
} }
kind = 2; kind = 2;
@ -267,7 +266,7 @@ static PyObject *_psyco_curs_validate_sql_basic(
if (!sql || !PyObject_IsTrue(sql)) { if (!sql || !PyObject_IsTrue(sql)) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't execute an empty query", NULL, NULL); "can't execute an empty query");
goto fail; goto fail;
} }
@ -338,8 +337,7 @@ _psyco_curs_merge_query_args(cursorObject *self,
if (!strcmp(s, "not enough arguments for format string") if (!strcmp(s, "not enough arguments for format string")
|| !strcmp(s, "not all arguments converted")) { || !strcmp(s, "not all arguments converted")) {
Dprintf("psyco_curs_execute: -> got a match"); Dprintf("psyco_curs_execute: -> got a match");
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self, s);
s, NULL, NULL);
pe = 1; pe = 1;
} }
@ -371,6 +369,7 @@ _psyco_curs_execute(cursorObject *self,
int res = -1; int res = -1;
int tmp; int tmp;
PyObject *fquery, *cvt = NULL; PyObject *fquery, *cvt = NULL;
const char *scroll;
operation = _psyco_curs_validate_sql_basic(self, operation); operation = _psyco_curs_validate_sql_basic(self, operation);
@ -379,13 +378,8 @@ _psyco_curs_execute(cursorObject *self,
if (operation == NULL) { goto exit; } if (operation == NULL) { goto exit; }
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
Py_CLEAR(self->query);
if (self->query) {
Py_DECREF(self->query);
self->query = NULL;
}
Dprintf("psyco_curs_execute: starting execution of new query"); Dprintf("psyco_curs_execute: starting execution of new query");
/* here we are, and we have a sequence or a dictionary filled with /* here we are, and we have a sequence or a dictionary filled with
@ -397,6 +391,21 @@ _psyco_curs_execute(cursorObject *self,
if (0 > _mogrify(vars, operation, self, &cvt)) { goto exit; } if (0 > _mogrify(vars, operation, self, &cvt)) { goto exit; }
} }
switch (self->scrollable) {
case -1:
scroll = "";
break;
case 0:
scroll = "NO SCROLL ";
break;
case 1:
scroll = "SCROLL ";
break;
default:
PyErr_SetString(InternalError, "unexpected scrollable value");
goto exit;
}
if (vars && cvt) { if (vars && cvt) {
if (!(fquery = _psyco_curs_merge_query_args(self, operation, cvt))) { if (!(fquery = _psyco_curs_merge_query_args(self, operation, cvt))) {
goto exit; goto exit;
@ -404,8 +413,9 @@ _psyco_curs_execute(cursorObject *self,
if (self->name != NULL) { if (self->name != NULL) {
self->query = Bytes_FromFormat( self->query = Bytes_FromFormat(
"DECLARE \"%s\" CURSOR %s HOLD FOR %s", "DECLARE \"%s\" %sCURSOR %s HOLD FOR %s",
self->name, self->name,
scroll,
self->withhold ? "WITH" : "WITHOUT", self->withhold ? "WITH" : "WITHOUT",
Bytes_AS_STRING(fquery)); Bytes_AS_STRING(fquery));
Py_DECREF(fquery); Py_DECREF(fquery);
@ -417,8 +427,9 @@ _psyco_curs_execute(cursorObject *self,
else { else {
if (self->name != NULL) { if (self->name != NULL) {
self->query = Bytes_FromFormat( self->query = Bytes_FromFormat(
"DECLARE \"%s\" CURSOR %s HOLD FOR %s", "DECLARE \"%s\" %sCURSOR %s HOLD FOR %s",
self->name, self->name,
scroll,
self->withhold ? "WITH" : "WITHOUT", self->withhold ? "WITH" : "WITHOUT",
Bytes_AS_STRING(operation)); Bytes_AS_STRING(operation));
} }
@ -462,15 +473,14 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
} }
if (self->name != NULL) { if (self->name != NULL) {
if (self->query != Py_None) { if (self->query) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't call .execute() on named cursors more than once", "can't call .execute() on named cursors more than once");
NULL, NULL);
return NULL; return NULL;
} }
if (self->conn->autocommit) { if (self->conn->autocommit) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't use a named cursor outside of transactions", NULL, NULL); "can't use a named cursor outside of transactions");
return NULL; return NULL;
} }
EXC_IF_NO_MARK(self); EXC_IF_NO_MARK(self);
@ -485,8 +495,7 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
} }
/* success */ /* success */
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
#define psyco_curs_executemany_doc \ #define psyco_curs_executemany_doc \
@ -515,7 +524,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
if (self->name != NULL) { if (self->name != NULL) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't call .executemany() on named cursors", NULL, NULL); "can't call .executemany() on named cursors");
return NULL; return NULL;
} }
@ -542,8 +551,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
self->rowcount = rowcount; self->rowcount = rowcount;
if (!PyErr_Occurred()) { if (!PyErr_Occurred()) {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
else { else {
return NULL; return NULL;
@ -743,7 +751,7 @@ exit:
} }
static PyObject * static PyObject *
psyco_curs_fetchone(cursorObject *self, PyObject *args) psyco_curs_fetchone(cursorObject *self)
{ {
PyObject *res; PyObject *res;
@ -767,8 +775,7 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount) { if (self->row >= self->rowcount) {
/* we exausted available data: return None */ /* we exausted available data: return None */
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
res = _psyco_curs_buildrow(self, self->row); res = _psyco_curs_buildrow(self, self->row);
@ -779,7 +786,7 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount if (self->row >= self->rowcount
&& self->conn->async_cursor && self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
return res; return res;
} }
@ -826,7 +833,7 @@ psyco_curs_next_named(cursorObject *self)
if (self->row >= self->rowcount if (self->row >= self->rowcount
&& self->conn->async_cursor && self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
return res; return res;
} }
@ -911,7 +918,7 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
if (self->row >= self->rowcount if (self->row >= self->rowcount
&& self->conn->async_cursor && self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
/* success */ /* success */
rv = list; rv = list;
@ -935,7 +942,7 @@ exit:
"Return `!None` when no more data is available.\n" "Return `!None` when no more data is available.\n"
static PyObject * static PyObject *
psyco_curs_fetchall(cursorObject *self, PyObject *args) psyco_curs_fetchall(cursorObject *self)
{ {
int i, size; int i, size;
PyObject *list = NULL; PyObject *list = NULL;
@ -980,7 +987,7 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args)
if (self->row >= self->rowcount if (self->row >= self->rowcount
&& self->conn->async_cursor && self->conn->async_cursor
&& PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self)
IFCLEARPGRES(self->pgres); CLEARPGRES(self->pgres);
/* success */ /* success */
rv = list; rv = list;
@ -1020,7 +1027,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args)
if (self->name != NULL) { if (self->name != NULL) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"can't call .callproc() on named cursors", NULL, NULL); "can't call .callproc() on named cursors");
goto exit; goto exit;
} }
@ -1067,7 +1074,7 @@ exit:
"sets) and will raise a NotSupportedError exception." "sets) and will raise a NotSupportedError exception."
static PyObject * static PyObject *
psyco_curs_nextset(cursorObject *self, PyObject *args) psyco_curs_nextset(cursorObject *self)
{ {
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
@ -1092,8 +1099,7 @@ psyco_curs_setinputsizes(cursorObject *self, PyObject *args)
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -1113,8 +1119,7 @@ psyco_curs_setoutputsize(cursorObject *self, PyObject *args)
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -1147,13 +1152,13 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
newpos = value; newpos = value;
} else { } else {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"scroll mode must be 'relative' or 'absolute'", NULL, NULL); "scroll mode must be 'relative' or 'absolute'");
return NULL; return NULL;
} }
if (newpos < 0 || newpos >= self->rowcount ) { if (newpos < 0 || newpos >= self->rowcount ) {
psyco_set_error(ProgrammingError, self, psyco_set_error(ProgrammingError, self,
"scroll destination out of bounds", NULL, NULL); "scroll destination out of bounds");
return NULL; return NULL;
} }
@ -1178,8 +1183,43 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
if (_psyco_curs_prefetch(self) < 0) return NULL; if (_psyco_curs_prefetch(self) < 0) return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None; }
#define psyco_curs_enter_doc \
"__enter__ -> self"
static PyObject *
psyco_curs_enter(cursorObject *self)
{
Py_INCREF(self);
return (PyObject *)self;
}
#define psyco_curs_exit_doc \
"__exit__ -- close the cursor"
static PyObject *
psyco_curs_exit(cursorObject *self, PyObject *args)
{
PyObject *tmp = NULL;
PyObject *rv = NULL;
/* don't care about the arguments here: don't need to parse them */
if (!(tmp = PyObject_CallMethod((PyObject *)self, "close", ""))) {
goto exit;
}
/* success (of curs.close()).
* Return None to avoid swallowing the exception */
rv = Py_None;
Py_INCREF(rv);
exit:
Py_XDECREF(tmp);
return rv;
} }
@ -1311,7 +1351,7 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
PyObject *file, *columns = NULL, *res = NULL; PyObject *file, *columns = NULL, *res = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O&s|ss" CONV_CODE_PY_SSIZE_T "O", kwlist, "O&s|ssnO", kwlist,
_psyco_curs_has_read_check, &file, &table_name, &sep, &null, &bufsize, _psyco_curs_has_read_check, &file, &table_name, &sep, &null, &bufsize,
&columns)) &columns))
{ {
@ -1327,14 +1367,12 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
goto exit; goto exit;
if (!(quoted_delimiter = psycopg_escape_string( if (!(quoted_delimiter = psycopg_escape_string(
(PyObject*)self->conn, sep, 0, NULL, NULL))) { self->conn, sep, 0, NULL, NULL))) {
PyErr_NoMemory();
goto exit; goto exit;
} }
if (!(quoted_null = psycopg_escape_string( if (!(quoted_null = psycopg_escape_string(
(PyObject*)self->conn, null, 0, NULL, NULL))) { self->conn, null, 0, NULL, NULL))) {
PyErr_NoMemory();
goto exit; goto exit;
} }
@ -1423,14 +1461,12 @@ psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
goto exit; goto exit;
if (!(quoted_delimiter = psycopg_escape_string( if (!(quoted_delimiter = psycopg_escape_string(
(PyObject*)self->conn, sep, 0, NULL, NULL))) { self->conn, sep, 0, NULL, NULL))) {
PyErr_NoMemory();
goto exit; goto exit;
} }
if (!(quoted_null = psycopg_escape_string( if (!(quoted_null = psycopg_escape_string(
(PyObject*)self->conn, null, 0, NULL, NULL))) { self->conn, null, 0, NULL, NULL))) {
PyErr_NoMemory();
goto exit; goto exit;
} }
@ -1488,7 +1524,7 @@ psyco_curs_copy_expert(cursorObject *self, PyObject *args, PyObject *kwargs)
static char *kwlist[] = {"sql", "file", "size", NULL}; static char *kwlist[] = {"sql", "file", "size", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"OO|" CONV_CODE_PY_SSIZE_T, kwlist, &sql, &file, &bufsize)) "OO|n", kwlist, &sql, &file, &bufsize))
{ return NULL; } { return NULL; }
EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_CLOSED(self);
@ -1567,22 +1603,70 @@ psyco_curs_withhold_get(cursorObject *self)
return ret; return ret;
} }
static int int
psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue) psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue)
{ {
int value; int value;
if (self->name == NULL) { if (pyvalue != Py_False && self->name == NULL) {
PyErr_SetString(ProgrammingError, PyErr_SetString(ProgrammingError,
"trying to set .withhold on unnamed cursor"); "trying to set .withhold on unnamed cursor");
return -1; return -1;
} }
if ((value = PyObject_IsTrue(pyvalue)) == -1) if ((value = PyObject_IsTrue(pyvalue)) == -1)
return -1; return -1;
self->withhold = value; self->withhold = value;
return 0;
}
#define psyco_curs_scrollable_doc \
"Set or return cursor use of SCROLL"
static PyObject *
psyco_curs_scrollable_get(cursorObject *self)
{
PyObject *ret = NULL;
switch (self->scrollable) {
case -1:
ret = Py_None;
break;
case 0:
ret = Py_False;
break;
case 1:
ret = Py_True;
break;
default:
PyErr_SetString(InternalError, "unexpected scrollable value");
}
Py_XINCREF(ret);
return ret;
}
int
psyco_curs_scrollable_set(cursorObject *self, PyObject *pyvalue)
{
int value;
if (pyvalue != Py_None && self->name == NULL) {
PyErr_SetString(ProgrammingError,
"trying to set .scrollable on unnamed cursor");
return -1;
}
if (pyvalue == Py_None) {
value = -1;
} else if ((value = PyObject_IsTrue(pyvalue)) == -1) {
return -1;
}
self->scrollable = value;
return 0; return 0;
} }
@ -1608,7 +1692,7 @@ cursor_next(PyObject *self)
if (NULL == ((cursorObject*)self)->name) { if (NULL == ((cursorObject*)self)->name) {
/* we don't parse arguments: psyco_curs_fetchone will do that for us */ /* we don't parse arguments: psyco_curs_fetchone will do that for us */
res = psyco_curs_fetchone((cursorObject*)self, NULL); res = psyco_curs_fetchone((cursorObject*)self);
/* convert a None to NULL to signal the end of iteration */ /* convert a None to NULL to signal the end of iteration */
if (res && res == Py_None) { if (res && res == Py_None) {
@ -1650,6 +1734,10 @@ static struct PyMethodDef cursorObject_methods[] = {
/* DBAPI-2.0 extensions */ /* DBAPI-2.0 extensions */
{"scroll", (PyCFunction)psyco_curs_scroll, {"scroll", (PyCFunction)psyco_curs_scroll,
METH_VARARGS|METH_KEYWORDS, psyco_curs_scroll_doc}, METH_VARARGS|METH_KEYWORDS, psyco_curs_scroll_doc},
{"__enter__", (PyCFunction)psyco_curs_enter,
METH_NOARGS, psyco_curs_enter_doc},
{"__exit__", (PyCFunction)psyco_curs_exit,
METH_VARARGS, psyco_curs_exit_doc},
/* psycopg extensions */ /* psycopg extensions */
#ifdef PSYCOPG_EXTENSIONS #ifdef PSYCOPG_EXTENSIONS
{"cast", (PyCFunction)psyco_curs_cast, {"cast", (PyCFunction)psyco_curs_cast,
@ -1712,6 +1800,10 @@ static struct PyGetSetDef cursorObject_getsets[] = {
(getter)psyco_curs_withhold_get, (getter)psyco_curs_withhold_get,
(setter)psyco_curs_withhold_set, (setter)psyco_curs_withhold_set,
psyco_curs_withhold_doc, NULL }, psyco_curs_withhold_doc, NULL },
{ "scrollable",
(getter)psyco_curs_scrollable_get,
(setter)psyco_curs_scrollable_set,
psyco_curs_scrollable_doc, NULL },
#endif #endif
{NULL} {NULL}
}; };
@ -1740,31 +1832,15 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
Py_INCREF(conn); Py_INCREF(conn);
self->conn = conn; self->conn = conn;
self->closed = 0;
self->withhold = 0;
self->mark = conn->mark; self->mark = conn->mark;
self->pgres = NULL;
self->notuples = 1; self->notuples = 1;
self->arraysize = 1; self->arraysize = 1;
self->itersize = 2000; self->itersize = 2000;
self->rowcount = -1; self->rowcount = -1;
self->lastoid = InvalidOid; self->lastoid = InvalidOid;
self->casts = NULL;
self->notice = NULL;
self->string_types = NULL;
self->binary_types = NULL;
self->weakreflist = NULL;
Py_INCREF(Py_None);
self->description = Py_None;
Py_INCREF(Py_None);
self->pgstatus = Py_None;
Py_INCREF(Py_None); Py_INCREF(Py_None);
self->tuple_factory = Py_None; self->tuple_factory = Py_None;
Py_INCREF(Py_None);
self->query = Py_None;
/* default tzinfo factory */ /* default tzinfo factory */
Py_INCREF(pyPsycopgTzFixedOffsetTimezone); Py_INCREF(pyPsycopgTzFixedOffsetTimezone);
@ -1777,30 +1853,39 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
return 0; return 0;
} }
static void static int
cursor_dealloc(PyObject* obj) cursor_clear(cursorObject *self)
{ {
cursorObject *self = (cursorObject *)obj;
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
PyObject_GC_UnTrack(self);
PyMem_Free(self->name);
Py_CLEAR(self->conn); Py_CLEAR(self->conn);
Py_CLEAR(self->casts);
Py_CLEAR(self->description); Py_CLEAR(self->description);
Py_CLEAR(self->pgstatus); Py_CLEAR(self->pgstatus);
Py_CLEAR(self->casts);
Py_CLEAR(self->caster);
Py_CLEAR(self->copyfile);
Py_CLEAR(self->tuple_factory); Py_CLEAR(self->tuple_factory);
Py_CLEAR(self->tzinfo_factory); Py_CLEAR(self->tzinfo_factory);
Py_CLEAR(self->query); Py_CLEAR(self->query);
Py_CLEAR(self->string_types); Py_CLEAR(self->string_types);
Py_CLEAR(self->binary_types); Py_CLEAR(self->binary_types);
return 0;
}
IFCLEARPGRES(self->pgres); static void
cursor_dealloc(PyObject* obj)
{
cursorObject *self = (cursorObject *)obj;
PyObject_GC_UnTrack(self);
if (self->weakreflist) {
PyObject_ClearWeakRefs(obj);
}
cursor_clear(self);
PyMem_Free(self->name);
CLEARPGRES(self->pgres);
Dprintf("cursor_dealloc: deleted cursor object at %p, refcnt = " Dprintf("cursor_dealloc: deleted cursor object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T, FORMAT_CODE_PY_SSIZE_T,
@ -1847,12 +1932,6 @@ cursor_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
cursor_del(PyObject* self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
cursor_repr(cursorObject *self) cursor_repr(cursorObject *self)
{ {
@ -1886,8 +1965,7 @@ cursor_traverse(cursorObject *self, visitproc visit, void *arg)
PyTypeObject cursorType = { PyTypeObject cursorType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.cursor", "psycopg2._psycopg.cursor",
sizeof(cursorObject), sizeof(cursorObject), 0,
0,
cursor_dealloc, /*tp_dealloc*/ cursor_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
@ -1898,47 +1976,30 @@ PyTypeObject cursorType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)cursor_repr, /*tp_str*/ (reprfunc)cursor_repr, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER |
Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS , Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS ,
/*tp_flags*/ /*tp_flags*/
cursorType_doc, /*tp_doc*/ cursorType_doc, /*tp_doc*/
(traverseproc)cursor_traverse, /*tp_traverse*/ (traverseproc)cursor_traverse, /*tp_traverse*/
0, /*tp_clear*/ (inquiry)cursor_clear, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
offsetof(cursorObject, weakreflist), /*tp_weaklistoffset*/ offsetof(cursorObject, weakreflist), /*tp_weaklistoffset*/
cursor_iter, /*tp_iter*/ cursor_iter, /*tp_iter*/
cursor_next, /*tp_iternext*/ cursor_next, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
cursorObject_methods, /*tp_methods*/ cursorObject_methods, /*tp_methods*/
cursorObject_members, /*tp_members*/ cursorObject_members, /*tp_members*/
cursorObject_getsets, /*tp_getset*/ cursorObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
cursor_init, /*tp_init*/ cursor_init, /*tp_init*/
0, /*tp_alloc Will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
cursor_new, /*tp_new*/ cursor_new, /*tp_new*/
(freefunc)cursor_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*/
}; };

40
psycopg/diagnostics.h Normal file
View File

@ -0,0 +1,40 @@
/* diagnostics.c - definition for the psycopg Diagnostics type
*
* Copyright (C) 2013 Matthew Woodcraft <matthew@woodcraft.me.uk>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 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 Lesser General Public
* License for more details.
*/
#ifndef PSYCOPG_DIAGNOSTICS_H
#define PSYCOPG_DIAGNOSTICS_H 1
#include "psycopg/error.h"
extern HIDDEN PyTypeObject diagnosticsType;
typedef struct {
PyObject_HEAD
errorObject *err; /* exception to retrieve the diagnostics from */
} diagnosticsObject;
#endif /* PSYCOPG_DIAGNOSTICS_H */

197
psycopg/diagnostics_type.c Normal file
View File

@ -0,0 +1,197 @@
/* diagnostics.c - present information from libpq error responses
*
* Copyright (C) 2013 Matthew Woodcraft <matthew@woodcraft.me.uk>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 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 Lesser General Public
* License for more details.
*/
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/diagnostics.h"
#include "psycopg/error.h"
/* These are new in PostgreSQL 9.3. Defining them here so that psycopg2 can
* use them with a 9.3+ server even if compiled against pre-9.3 headers. */
#ifndef PG_DIAG_SCHEMA_NAME
#define PG_DIAG_SCHEMA_NAME 's'
#endif
#ifndef PG_DIAG_TABLE_NAME
#define PG_DIAG_TABLE_NAME 't'
#endif
#ifndef PG_DIAG_COLUMN_NAME
#define PG_DIAG_COLUMN_NAME 'c'
#endif
#ifndef PG_DIAG_DATATYPE_NAME
#define PG_DIAG_DATATYPE_NAME 'd'
#endif
#ifndef PG_DIAG_CONSTRAINT_NAME
#define PG_DIAG_CONSTRAINT_NAME 'n'
#endif
/* Retrieve an error string from the exception's cursor.
*
* If the cursor or its result isn't available, return None.
*/
static PyObject *
psyco_diagnostics_get_field(diagnosticsObject *self, void *closure)
{
const char *errortext;
if (!self->err->pgres) {
Py_RETURN_NONE;
}
errortext = PQresultErrorField(self->err->pgres, (Py_intptr_t) closure);
return error_text_from_chars(self->err, errortext);
}
/* object calculated member list */
static struct PyGetSetDef diagnosticsObject_getsets[] = {
{ "severity", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SEVERITY },
{ "sqlstate", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SQLSTATE },
{ "message_primary", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_MESSAGE_PRIMARY },
{ "message_detail", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_MESSAGE_DETAIL },
{ "message_hint", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_MESSAGE_HINT },
{ "statement_position", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_STATEMENT_POSITION },
{ "internal_position", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_INTERNAL_POSITION },
{ "internal_query", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_INTERNAL_QUERY },
{ "context", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_CONTEXT },
{ "schema_name", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SCHEMA_NAME },
{ "table_name", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_TABLE_NAME },
{ "column_name", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_COLUMN_NAME },
{ "datatype_name", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_DATATYPE_NAME },
{ "constraint_name", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_CONSTRAINT_NAME },
{ "source_file", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SOURCE_FILE },
{ "source_line", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SOURCE_LINE },
{ "source_function", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SOURCE_FUNCTION },
{NULL}
};
/* initialization and finalization methods */
static PyObject *
diagnostics_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static int
diagnostics_init(diagnosticsObject *self, PyObject *args, PyObject *kwds)
{
PyObject *err = NULL;
if (!PyArg_ParseTuple(args, "O", &err))
return -1;
if (!PyObject_TypeCheck(err, &errorType)) {
PyErr_SetString(PyExc_TypeError,
"The argument must be a psycopg2.Error");
return -1;
}
Py_INCREF(err);
self->err = (errorObject *)err;
return 0;
}
static void
diagnostics_dealloc(diagnosticsObject* self)
{
Py_CLEAR(self->err);
Py_TYPE(self)->tp_free((PyObject *)self);
}
/* object type */
static const char diagnosticsType_doc[] =
"Details from a database error report.\n\n"
"The object is returned by the `~psycopg2.Error.diag` attribute of the\n"
"`!Error` object.\n"
"All the information available from the |PQresultErrorField|_ function\n"
"are exposed as attributes by the object, e.g. the `!severity` attribute\n"
"returns the `!PG_DIAG_SEVERITY` code. "
"Please refer to the `PostgreSQL documentation`__ for the meaning of all"
" the attributes.\n\n"
".. |PQresultErrorField| replace:: `!PQresultErrorField()`\n"
".. _PQresultErrorField: http://www.postgresql.org/docs/current/static/"
"libpq-exec.html#LIBPQ-PQRESULTERRORFIELD\n"
".. __: PQresultErrorField_\n";
PyTypeObject diagnosticsType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.Diagnostics",
sizeof(diagnosticsObject), 0,
(destructor)diagnostics_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*/
diagnosticsType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
0, /*tp_methods*/
0, /*tp_members*/
diagnosticsObject_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)diagnostics_init, /*tp_init*/
0, /*tp_alloc*/
diagnostics_new, /*tp_new*/
};

43
psycopg/error.h Normal file
View File

@ -0,0 +1,43 @@
/* error.h - definition for the psycopg base Error type
*
* Copyright (C) 2013 Daniele Varrazzo <daniele.varrazzo@gmail.com>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 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 Lesser General Public
* License for more details.
*/
#ifndef PSYCOPG_ERROR_H
#define PSYCOPG_ERROR_H 1
extern HIDDEN PyTypeObject errorType;
typedef struct {
PyBaseExceptionObject exc;
PyObject *pgerror;
PyObject *pgcode;
cursorObject *cursor;
char *codec;
PGresult *pgres;
} errorObject;
HIDDEN PyObject *error_text_from_chars(errorObject *self, const char *str);
#endif /* PSYCOPG_ERROR_H */

276
psycopg/error_type.c Normal file
View File

@ -0,0 +1,276 @@
/* error_type.c - python interface to the Error objects
*
* Copyright (C) 2013 Daniele Varrazzo <daniele.varrazzo@gmail.com>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 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 Lesser General Public
* License for more details.
*/
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/error.h"
#include "psycopg/diagnostics.h"
#include "psycopg/pqpath.h"
PyObject *
error_text_from_chars(errorObject *self, const char *str)
{
if (str == NULL) {
Py_INCREF(Py_None);
return (Py_None);
}
#if PY_MAJOR_VERSION < 3
return PyString_FromString(str);
#else
return PyUnicode_Decode(str, strlen(str),
self->codec ? self->codec : "ascii", "replace");
#endif
}
static const char pgerror_doc[] =
"The error message returned by the backend, if available, else None";
static const char pgcode_doc[] =
"The error code returned by the backend, if available, else None";
static const char cursor_doc[] =
"The cursor that raised the exception, if available, else None";
static const char diag_doc[] =
"A Diagnostics object to get further information about the error";
static PyMemberDef error_members[] = {
{ "pgerror", T_OBJECT, offsetof(errorObject, pgerror),
READONLY, (char *)pgerror_doc },
{ "pgcode", T_OBJECT, offsetof(errorObject, pgcode),
READONLY, (char *)pgcode_doc },
{ "cursor", T_OBJECT, offsetof(errorObject, cursor),
READONLY, (char *)cursor_doc },
{ NULL }
};
static PyObject *
error_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
return ((PyTypeObject *)PyExc_StandardError)->tp_new(
type, args, kwargs);
}
static int
error_init(errorObject *self, PyObject *args, PyObject *kwargs)
{
if (((PyTypeObject *)PyExc_StandardError)->tp_init(
(PyObject *)self, args, kwargs) < 0) {
return -1;
}
return 0;
}
static int
error_traverse(errorObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->pgerror);
Py_VISIT(self->pgcode);
Py_VISIT(self->cursor);
return ((PyTypeObject *)PyExc_StandardError)->tp_traverse(
(PyObject *)self, visit, arg);
}
static int
error_clear(errorObject *self)
{
Py_CLEAR(self->pgerror);
Py_CLEAR(self->pgcode);
Py_CLEAR(self->cursor);
return ((PyTypeObject *)PyExc_StandardError)->tp_clear((PyObject *)self);
}
static void
error_dealloc(errorObject *self)
{
PyObject_GC_UnTrack((PyObject *)self);
error_clear(self);
PyMem_Free(self->codec);
CLEARPGRES(self->pgres);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *
error_get_diag(errorObject *self, void *closure)
{
return PyObject_CallFunctionObjArgs(
(PyObject *)&diagnosticsType, (PyObject *)self, NULL);
}
static struct PyGetSetDef error_getsets[] = {
{ "diag", (getter)error_get_diag, NULL, (char *)diag_doc },
{ NULL }
};
/* Error.__reduce__
*
* The method is required to make exceptions picklable: set the cursor
* attribute to None. Only working from Py 2.5: previous versions
* would require implementing __getstate__, and as of 2012 it's a little
* bit too late to care. */
static PyObject *
psyco_error_reduce(errorObject *self)
{
PyObject *meth = NULL;
PyObject *tuple = NULL;
PyObject *dict = NULL;
PyObject *rv = NULL;
if (!(meth = PyObject_GetAttrString(PyExc_StandardError, "__reduce__"))) {
goto error;
}
if (!(tuple = PyObject_CallFunctionObjArgs(meth, self, NULL))) {
goto error;
}
/* tuple is (type, args): convert it to (type, args, dict)
* with our extra items in the dict.
*
* If these checks fail, we can still return a valid object. Pickle
* will likely fail downstream, but there's nothing else we can do here */
if (!PyTuple_Check(tuple)) { goto exit; }
if (2 != PyTuple_GET_SIZE(tuple)) { goto exit; }
if (!(dict = PyDict_New())) { goto error; }
if (0 != PyDict_SetItemString(dict, "pgerror", self->pgerror)) { goto error; }
if (0 != PyDict_SetItemString(dict, "pgcode", self->pgcode)) { goto error; }
{
PyObject *newtuple;
if (!(newtuple = PyTuple_Pack(3,
PyTuple_GET_ITEM(tuple, 0),
PyTuple_GET_ITEM(tuple, 1),
dict))) {
goto error;
}
Py_DECREF(tuple);
tuple = newtuple;
}
exit:
rv = tuple;
tuple = NULL;
error:
Py_XDECREF(dict);
Py_XDECREF(tuple);
Py_XDECREF(meth);
return rv;
}
PyObject *
psyco_error_setstate(errorObject *self, PyObject *state)
{
PyObject *rv = NULL;
/* we don't call the StandartError's setstate as it would try to load the
* dict content as attributes */
if (state == Py_None) {
goto exit;
}
if (!PyDict_Check(state)) {
PyErr_SetString(PyExc_TypeError, "state is not a dictionary");
goto error;
}
/* load the dict content in the structure */
Py_CLEAR(self->pgerror);
self->pgerror = PyDict_GetItemString(state, "pgerror");
Py_XINCREF(self->pgerror);
Py_CLEAR(self->pgcode);
self->pgcode = PyDict_GetItemString(state, "pgcode");
Py_XINCREF(self->pgcode);
Py_CLEAR(self->cursor);
/* We never expect a cursor in the state as it's not picklable.
* at most there could be a None here, coming from Psycopg < 2.5 */
exit:
rv = Py_None;
Py_INCREF(rv);
error:
return rv;
}
static PyMethodDef error_methods[] = {
/* Make Error and all its subclasses picklable. */
{"__reduce__", (PyCFunction)psyco_error_reduce, METH_NOARGS },
{"__setstate__", (PyCFunction)psyco_error_setstate, METH_O },
{NULL}
};
PyTypeObject errorType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.Error",
sizeof(errorObject), 0,
(destructor)error_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|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
Error_doc, /*tp_doc*/
(traverseproc)error_traverse, /*tp_traverse*/
(inquiry)error_clear, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
error_methods, /*tp_methods*/
error_members, /*tp_members*/
error_getsets, /*tp_getset*/
0, /*tp_base Will be set to StandardError in module init */
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)error_init, /*tp_init*/
0, /*tp_alloc*/
error_new, /*tp_new*/
};

View File

@ -53,8 +53,7 @@ psyco_set_wait_callback(PyObject *self, PyObject *obj)
wait_callback = NULL; wait_callback = NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }

View File

@ -78,13 +78,13 @@ RAISES_NEG HIDDEN int lobject_close(lobjectObject *self);
#define EXC_IF_LOBJ_LEVEL0(self) \ #define EXC_IF_LOBJ_LEVEL0(self) \
if (self->conn->autocommit) { \ if (self->conn->autocommit) { \
psyco_set_error(ProgrammingError, NULL, \ psyco_set_error(ProgrammingError, NULL, \
"can't use a lobject outside of transactions", NULL, NULL); \ "can't use a lobject outside of transactions"); \
return NULL; \ return NULL; \
} }
#define EXC_IF_LOBJ_UNMARKED(self) \ #define EXC_IF_LOBJ_UNMARKED(self) \
if (self->conn->mark != self->mark) { \ if (self->conn->mark != self->mark) { \
psyco_set_error(ProgrammingError, NULL, \ psyco_set_error(ProgrammingError, NULL, \
"lobject isn't valid anymore", NULL, NULL); \ "lobject isn't valid anymore"); \
return NULL; \ return NULL; \
} }

View File

@ -59,8 +59,7 @@ psyco_lobj_close(lobjectObject *self, PyObject *args)
return NULL; return NULL;
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* write method - write data to the lobject */ /* write method - write data to the lobject */
@ -128,7 +127,7 @@ psyco_lobj_read(lobjectObject *self, PyObject *args)
Py_ssize_t size = -1; Py_ssize_t size = -1;
char *buffer; char *buffer;
if (!PyArg_ParseTuple(args, "|" CONV_CODE_PY_SSIZE_T, &size)) return NULL; if (!PyArg_ParseTuple(args, "|n", &size)) return NULL;
EXC_IF_LOBJ_CLOSED(self); EXC_IF_LOBJ_CLOSED(self);
EXC_IF_LOBJ_LEVEL0(self); EXC_IF_LOBJ_LEVEL0(self);
@ -213,10 +212,9 @@ static PyObject *
psyco_lobj_unlink(lobjectObject *self, PyObject *args) psyco_lobj_unlink(lobjectObject *self, PyObject *args)
{ {
if (lobject_unlink(self) < 0) if (lobject_unlink(self) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* export method - export lobject's content to given file */ /* export method - export lobject's content to given file */
@ -230,15 +228,14 @@ psyco_lobj_export(lobjectObject *self, PyObject *args)
const char *filename; const char *filename;
if (!PyArg_ParseTuple(args, "s", &filename)) if (!PyArg_ParseTuple(args, "s", &filename))
return NULL; return NULL;
EXC_IF_LOBJ_LEVEL0(self); EXC_IF_LOBJ_LEVEL0(self);
if (lobject_export(self, filename) < 0) if (lobject_export(self, filename) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -272,8 +269,7 @@ psyco_lobj_truncate(lobjectObject *self, PyObject *args)
if (lobject_truncate(self, len) < 0) if (lobject_truncate(self, len) < 0)
return NULL; return NULL;
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
#endif /* PG_VERSION_HEX >= 0x080300 */ #endif /* PG_VERSION_HEX >= 0x080300 */
@ -333,7 +329,7 @@ lobject_setup(lobjectObject *self, connectionObject *conn,
if (conn->autocommit) { if (conn->autocommit) {
psyco_set_error(ProgrammingError, NULL, psyco_set_error(ProgrammingError, NULL,
"can't use a lobject outside of transactions", NULL, NULL); "can't use a lobject outside of transactions");
return -1; return -1;
} }
@ -392,12 +388,6 @@ lobject_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
lobject_del(PyObject* self)
{
PyObject_Del(self);
}
static PyObject * static PyObject *
lobject_repr(lobjectObject *self) lobject_repr(lobjectObject *self)
{ {
@ -414,8 +404,7 @@ lobject_repr(lobjectObject *self)
PyTypeObject lobjectType = { PyTypeObject lobjectType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.lobject", "psycopg2._psycopg.lobject",
sizeof(lobjectObject), sizeof(lobjectObject), 0,
0,
lobject_dealloc, /*tp_dealloc*/ lobject_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
@ -426,47 +415,30 @@ PyTypeObject lobjectType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)lobject_repr, /*tp_str*/ (reprfunc)lobject_repr, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
lobjectType_doc, /*tp_doc*/ lobjectType_doc, /*tp_doc*/
0, /*tp_traverse*/ 0, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
lobjectObject_methods, /*tp_methods*/ lobjectObject_methods, /*tp_methods*/
lobjectObject_members, /*tp_members*/ lobjectObject_members, /*tp_members*/
lobjectObject_getsets, /*tp_getset*/ lobjectObject_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
lobject_init, /*tp_init*/ lobject_init, /*tp_init*/
0, /*tp_alloc Will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
lobject_new, /*tp_new*/ lobject_new, /*tp_new*/
(freefunc)lobject_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*/
}; };
#endif #endif

View File

@ -203,7 +203,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
/* else set the right exception and return NULL */ /* else set the right exception and return NULL */
PyOS_snprintf(buffer, 255, "can't adapt type '%s'", PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
Py_TYPE(obj)->tp_name); Py_TYPE(obj)->tp_name);
psyco_set_error(ProgrammingError, NULL, buffer, NULL, NULL); psyco_set_error(ProgrammingError, NULL, buffer);
return NULL; return NULL;
} }

View File

@ -42,8 +42,7 @@
static PyObject * static PyObject *
psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args) psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args)
{ {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* getbinary - return quoted representation for object */ /* getbinary - return quoted representation for object */
@ -54,8 +53,7 @@ psyco_isqlquote_getquoted(isqlquoteObject *self, PyObject *args)
static PyObject * static PyObject *
psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args) psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args)
{ {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
/* getbuffer - return quoted representation for object */ /* getbuffer - return quoted representation for object */
@ -66,8 +64,7 @@ psyco_isqlquote_getbinary(isqlquoteObject *self, PyObject *args)
static PyObject * static PyObject *
psyco_isqlquote_getbuffer(isqlquoteObject *self, PyObject *args) psyco_isqlquote_getbuffer(isqlquoteObject *self, PyObject *args)
{ {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -135,12 +132,6 @@ isqlquote_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
} }
static void
isqlquote_del(PyObject* self)
{
PyObject_Del(self);
}
/* object type */ /* object type */
@ -152,8 +143,7 @@ isqlquote_del(PyObject* self)
PyTypeObject isqlquoteType = { PyTypeObject isqlquoteType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.ISQLQuote", "psycopg2._psycopg.ISQLQuote",
sizeof(isqlquoteObject), sizeof(isqlquoteObject), 0,
0,
isqlquote_dealloc, /*tp_dealloc*/ isqlquote_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
@ -164,45 +154,28 @@ PyTypeObject isqlquoteType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
isqlquoteType_doc, /*tp_doc*/ isqlquoteType_doc, /*tp_doc*/
0, /*tp_traverse*/ 0, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
isqlquoteObject_methods, /*tp_methods*/ isqlquoteObject_methods, /*tp_methods*/
isqlquoteObject_members, /*tp_members*/ isqlquoteObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
isqlquote_init, /*tp_init*/ isqlquote_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
isqlquote_new, /*tp_new*/ isqlquote_new, /*tp_new*/
(freefunc)isqlquote_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
}; };

View File

@ -26,7 +26,7 @@
#ifndef PSYCOPG_NOTIFY_H #ifndef PSYCOPG_NOTIFY_H
#define PSYCOPG_NOTIFY_H 1 #define PSYCOPG_NOTIFY_H 1
extern HIDDEN PyTypeObject NotifyType; extern HIDDEN PyTypeObject notifyType;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -35,6 +35,6 @@ typedef struct {
PyObject *channel; PyObject *channel;
PyObject *payload; PyObject *payload;
} NotifyObject; } notifyObject;
#endif /* PSYCOPG_NOTIFY_H */ #endif /* PSYCOPG_NOTIFY_H */

View File

@ -52,22 +52,20 @@ static const char payload_doc[] =
"of the server this member is always the empty string."; "of the server this member is always the empty string.";
static PyMemberDef notify_members[] = { static PyMemberDef notify_members[] = {
{ "pid", T_OBJECT, offsetof(NotifyObject, pid), READONLY, (char *)pid_doc }, { "pid", T_OBJECT, offsetof(notifyObject, pid), READONLY, (char *)pid_doc },
{ "channel", T_OBJECT, offsetof(NotifyObject, channel), READONLY, (char *)channel_doc }, { "channel", T_OBJECT, offsetof(notifyObject, channel), READONLY, (char *)channel_doc },
{ "payload", T_OBJECT, offsetof(NotifyObject, payload), READONLY, (char *)payload_doc }, { "payload", T_OBJECT, offsetof(notifyObject, payload), READONLY, (char *)payload_doc },
{ NULL } { NULL }
}; };
static PyObject * static PyObject *
notify_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) notify_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{ {
NotifyObject *self = (NotifyObject *)type->tp_alloc(type, 0); return type->tp_alloc(type, 0);
return (PyObject *)self;
} }
static int static int
notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs) notify_init(notifyObject *self, PyObject *args, PyObject *kwargs)
{ {
static char *kwlist[] = {"pid", "channel", "payload", NULL}; static char *kwlist[] = {"pid", "channel", "payload", NULL};
PyObject *pid = NULL, *channel = NULL, *payload = NULL; PyObject *pid = NULL, *channel = NULL, *payload = NULL;
@ -81,32 +79,20 @@ notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs)
payload = Text_FromUTF8(""); payload = Text_FromUTF8("");
} }
Py_CLEAR(self->pid);
Py_INCREF(pid); Py_INCREF(pid);
self->pid = pid; self->pid = pid;
Py_CLEAR(self->channel);
Py_INCREF(channel); Py_INCREF(channel);
self->channel = channel; self->channel = channel;
Py_CLEAR(self->payload);
Py_INCREF(payload); Py_INCREF(payload);
self->payload = payload; self->payload = payload;
return 0; return 0;
} }
static int
notify_traverse(NotifyObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->pid);
Py_VISIT(self->channel);
Py_VISIT(self->payload);
return 0;
}
static void static void
notify_dealloc(NotifyObject *self) notify_dealloc(notifyObject *self)
{ {
Py_CLEAR(self->pid); Py_CLEAR(self->pid);
Py_CLEAR(self->channel); Py_CLEAR(self->channel);
@ -115,16 +101,10 @@ notify_dealloc(NotifyObject *self)
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free((PyObject *)self);
} }
static void
notify_del(PyObject *self)
{
PyObject_GC_Del(self);
}
/* Convert a notify into a 2 or 3 items tuple. */ /* Convert a notify into a 2 or 3 items tuple. */
static PyObject * static PyObject *
notify_astuple(NotifyObject *self, int with_payload) notify_astuple(notifyObject *self, int with_payload)
{ {
PyObject *tself; PyObject *tself;
if (!(tself = PyTuple_New(with_payload ? 3 : 2))) { return NULL; } if (!(tself = PyTuple_New(with_payload ? 3 : 2))) { return NULL; }
@ -162,15 +142,15 @@ notify_astuple(NotifyObject *self, int with_payload)
* the (pid, channel) pair is no more equivalent as dict key to the Notify. * the (pid, channel) pair is no more equivalent as dict key to the Notify.
*/ */
static PyObject * static PyObject *
notify_richcompare(NotifyObject *self, PyObject *other, int op) notify_richcompare(notifyObject *self, PyObject *other, int op)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *tself = NULL; PyObject *tself = NULL;
PyObject *tother = NULL; PyObject *tother = NULL;
if (Py_TYPE(other) == &NotifyType) { if (Py_TYPE(other) == &notifyType) {
if (!(tself = notify_astuple(self, 1))) { goto exit; } if (!(tself = notify_astuple(self, 1))) { goto exit; }
if (!(tother = notify_astuple((NotifyObject *)other, 1))) { goto exit; } if (!(tother = notify_astuple((notifyObject *)other, 1))) { goto exit; }
rv = PyObject_RichCompare(tself, tother, op); rv = PyObject_RichCompare(tself, tother, op);
} }
else if (PyTuple_Check(other)) { else if (PyTuple_Check(other)) {
@ -190,7 +170,7 @@ exit:
static Py_hash_t static Py_hash_t
notify_hash(NotifyObject *self) notify_hash(notifyObject *self)
{ {
Py_hash_t rv = -1L; Py_hash_t rv = -1L;
PyObject *tself = NULL; PyObject *tself = NULL;
@ -207,7 +187,7 @@ exit:
static PyObject* static PyObject*
notify_repr(NotifyObject *self) notify_repr(notifyObject *self)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *format = NULL; PyObject *format = NULL;
@ -237,13 +217,13 @@ exit:
/* Notify can be accessed as a 2 items tuple for backward compatibility */ /* Notify can be accessed as a 2 items tuple for backward compatibility */
static Py_ssize_t static Py_ssize_t
notify_len(NotifyObject *self) notify_len(notifyObject *self)
{ {
return 2; return 2;
} }
static PyObject * static PyObject *
notify_getitem(NotifyObject *self, Py_ssize_t item) notify_getitem(notifyObject *self, Py_ssize_t item)
{ {
if (item < 0) if (item < 0)
item += 2; item += 2;
@ -275,66 +255,45 @@ static PySequenceMethods notify_sequence = {
}; };
PyTypeObject NotifyType = { PyTypeObject notifyType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Notify", "psycopg2.extensions.Notify",
sizeof(NotifyObject), sizeof(notifyObject), 0,
0,
(destructor)notify_dealloc, /* tp_dealloc */ (destructor)notify_dealloc, /* tp_dealloc */
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)notify_repr, /*tp_repr*/ (reprfunc)notify_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
&notify_sequence, /*tp_as_sequence*/ &notify_sequence, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
(hashfunc)notify_hash, /*tp_hash */ (hashfunc)notify_hash, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
/* Notify is not GC as it only has string attributes */
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
notify_doc, /*tp_doc*/ notify_doc, /*tp_doc*/
0, /*tp_traverse*/
(traverseproc)notify_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
(richcmpfunc)notify_richcompare, /*tp_richcompare*/ (richcmpfunc)notify_richcompare, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
0, /*tp_methods*/ 0, /*tp_methods*/
notify_members, /*tp_members*/ notify_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
(initproc)notify_init, /*tp_init*/ (initproc)notify_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
notify_new, /*tp_new*/ notify_new, /*tp_new*/
(freefunc)notify_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
}; };

View File

@ -38,6 +38,7 @@
#include "psycopg/green.h" #include "psycopg/green.h"
#include "psycopg/typecast.h" #include "psycopg/typecast.h"
#include "psycopg/pgtypes.h" #include "psycopg/pgtypes.h"
#include "psycopg/error.h"
#include <string.h> #include <string.h>
@ -149,15 +150,20 @@ exception_from_sqlstate(const char *sqlstate)
/* pq_raise - raise a python exception of the right kind /* pq_raise - raise a python exception of the right kind
This function should be called while holding the GIL. */ This function should be called while holding the GIL.
The function passes the ownership of the pgres to the returned exception,
wherer the pgres was the explicit argument or taken from the cursor.
So, after calling it curs->pgres will be set to null */
RAISES static void RAISES static void
pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres) pq_raise(connectionObject *conn, cursorObject *curs, PGresult **pgres)
{ {
PyObject *exc = NULL; PyObject *exc = NULL;
const char *err = NULL; const char *err = NULL;
const char *err2 = NULL; const char *err2 = NULL;
const char *code = NULL; const char *code = NULL;
PyObject *pyerr = NULL;
if (conn == NULL) { if (conn == NULL) {
PyErr_SetString(DatabaseError, PyErr_SetString(DatabaseError,
@ -171,13 +177,13 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
conn->closed = 2; conn->closed = 2;
if (pgres == NULL && curs != NULL) if (pgres == NULL && curs != NULL)
pgres = curs->pgres; pgres = &curs->pgres;
if (pgres) { if (pgres && *pgres) {
err = PQresultErrorMessage(pgres); err = PQresultErrorMessage(*pgres);
if (err != NULL) { if (err != NULL) {
Dprintf("pq_raise: PQresultErrorMessage: err=%s", err); Dprintf("pq_raise: PQresultErrorMessage: err=%s", err);
code = PQresultErrorField(pgres, PG_DIAG_SQLSTATE); code = PQresultErrorField(*pgres, PG_DIAG_SQLSTATE);
} }
} }
if (err == NULL) { if (err == NULL) {
@ -210,7 +216,26 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
err2 = strip_severity(err); err2 = strip_severity(err);
Dprintf("pq_raise: err2=%s", err2); Dprintf("pq_raise: err2=%s", err2);
psyco_set_error(exc, curs, err2, err, code); pyerr = psyco_set_error(exc, curs, err2);
if (pyerr && PyObject_TypeCheck(pyerr, &errorType)) {
errorObject *perr = (errorObject *)pyerr;
PyMem_Free(perr->codec);
psycopg_strdup(&perr->codec, conn->codec, 0);
Py_CLEAR(perr->pgerror);
perr->pgerror = error_text_from_chars(perr, err);
Py_CLEAR(perr->pgcode);
perr->pgcode = error_text_from_chars(perr, code);
CLEARPGRES(perr->pgres);
if (pgres && *pgres) {
perr->pgres = *pgres;
*pgres = NULL;
}
}
} }
/* pq_set_critical, pq_resolve_critical - manage critical errors /* pq_set_critical, pq_resolve_critical - manage critical errors
@ -369,7 +394,7 @@ pq_execute_command_locked(connectionObject *conn, const char *query,
} }
retvalue = 0; retvalue = 0;
IFCLEARPGRES(*pgres); CLEARPGRES(*pgres);
cleanup: cleanup:
return retvalue; return retvalue;
@ -388,14 +413,16 @@ pq_complete_error(connectionObject *conn, PGresult **pgres, char **error)
{ {
Dprintf("pq_complete_error: pgconn = %p, pgres = %p, error = %s", Dprintf("pq_complete_error: pgconn = %p, pgres = %p, error = %s",
conn->pgconn, *pgres, *error ? *error : "(null)"); conn->pgconn, *pgres, *error ? *error : "(null)");
if (*pgres != NULL) if (*pgres != NULL) {
pq_raise(conn, NULL, *pgres); pq_raise(conn, NULL, pgres);
/* now *pgres is null */
}
else if (*error != NULL) { else if (*error != NULL) {
PyErr_SetString(OperationalError, *error); PyErr_SetString(OperationalError, *error);
} else { } else {
PyErr_SetString(OperationalError, "unknown error"); PyErr_SetString(OperationalError, "unknown error");
} }
IFCLEARPGRES(*pgres);
if (*error) { if (*error) {
free(*error); free(*error);
*error = NULL; *error = NULL;
@ -557,12 +584,18 @@ pq_reset_locked(connectionObject *conn, PGresult **pgres, char **error,
if (retvalue != 0) return retvalue; if (retvalue != 0) return retvalue;
} }
retvalue = pq_execute_command_locked(conn, "RESET ALL", pgres, error, tstate); if (conn->server_version >= 80300) {
if (retvalue != 0) return retvalue; retvalue = pq_execute_command_locked(conn, "DISCARD ALL", pgres, error, tstate);
if (retvalue != 0) return retvalue;
}
else {
retvalue = pq_execute_command_locked(conn, "RESET ALL", pgres, error, tstate);
if (retvalue != 0) return retvalue;
retvalue = pq_execute_command_locked(conn, retvalue = pq_execute_command_locked(conn,
"SET SESSION AUTHORIZATION DEFAULT", pgres, error, tstate); "SET SESSION AUTHORIZATION DEFAULT", pgres, error, tstate);
if (retvalue != 0) return retvalue; if (retvalue != 0) return retvalue;
}
/* should set the tpc xid to null: postponed until we get the GIL again */ /* should set the tpc xid to null: postponed until we get the GIL again */
conn->status = CONN_STATUS_READY; conn->status = CONN_STATUS_READY;
@ -717,7 +750,7 @@ pq_tpc_command_locked(connectionObject *conn, const char *cmd, const char *tid,
PyEval_RestoreThread(*tstate); PyEval_RestoreThread(*tstate);
/* convert the xid into the postgres transaction_id and quote it. */ /* convert the xid into the postgres transaction_id and quote it. */
if (!(etid = psycopg_escape_string((PyObject *)conn, tid, 0, NULL, NULL))) if (!(etid = psycopg_escape_string(conn, tid, 0, NULL, NULL)))
{ goto exit; } { goto exit; }
/* prepare the command to the server */ /* prepare the command to the server */
@ -869,7 +902,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result)
} }
if (async == 0) { if (async == 0) {
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
Dprintf("pq_execute: executing SYNC query: pgconn = %p", curs->conn->pgconn); Dprintf("pq_execute: executing SYNC query: pgconn = %p", curs->conn->pgconn);
Dprintf(" %-.200s", query); Dprintf(" %-.200s", query);
if (!psyco_green()) { if (!psyco_green()) {
@ -908,7 +941,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result)
Dprintf("pq_execute: executing ASYNC query: pgconn = %p", curs->conn->pgconn); Dprintf("pq_execute: executing ASYNC query: pgconn = %p", curs->conn->pgconn);
Dprintf(" %-.200s", query); Dprintf(" %-.200s", query);
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
if (PQsendQuery(curs->conn->pgconn, query) == 0) { if (PQsendQuery(curs->conn->pgconn, query) == 0) {
pthread_mutex_unlock(&(curs->conn->lock)); pthread_mutex_unlock(&(curs->conn->lock));
Py_BLOCK_THREADS; Py_BLOCK_THREADS;
@ -1305,7 +1338,7 @@ _pq_copy_in_v3(cursorObject *curs)
/* XXX would be nice to propagate the exeption */ /* XXX would be nice to propagate the exeption */
res = PQputCopyEnd(curs->conn->pgconn, "error in .read() call"); res = PQputCopyEnd(curs->conn->pgconn, "error in .read() call");
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
Dprintf("_pq_copy_in_v3: copy ended; res = %d", res); Dprintf("_pq_copy_in_v3: copy ended; res = %d", res);
@ -1329,7 +1362,7 @@ _pq_copy_in_v3(cursorObject *curs)
break; break;
if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR) if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR)
pq_raise(curs->conn, curs, NULL); pq_raise(curs->conn, curs, NULL);
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
} }
} }
@ -1395,7 +1428,7 @@ _pq_copy_out_v3(cursorObject *curs)
} }
/* and finally we grab the operation result from the backend */ /* and finally we grab the operation result from the backend */
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
for (;;) { for (;;) {
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
curs->pgres = PQgetResult(curs->conn->pgconn); curs->pgres = PQgetResult(curs->conn->pgconn);
@ -1405,7 +1438,7 @@ _pq_copy_out_v3(cursorObject *curs)
break; break;
if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR) if (PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR)
pq_raise(curs->conn, curs, NULL); pq_raise(curs->conn, curs, NULL);
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
} }
ret = 1; ret = 1;
@ -1466,7 +1499,7 @@ pq_fetch(cursorObject *curs, int no_result)
curs->rowcount = -1; curs->rowcount = -1;
/* error caught by out glorious notice handler */ /* error caught by out glorious notice handler */
if (PyErr_Occurred()) ex = -1; if (PyErr_Occurred()) ex = -1;
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
break; break;
case PGRES_COPY_IN: case PGRES_COPY_IN:
@ -1475,7 +1508,7 @@ pq_fetch(cursorObject *curs, int no_result)
curs->rowcount = -1; curs->rowcount = -1;
/* error caught by out glorious notice handler */ /* error caught by out glorious notice handler */
if (PyErr_Occurred()) ex = -1; if (PyErr_Occurred()) ex = -1;
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
break; break;
case PGRES_TUPLES_OK: case PGRES_TUPLES_OK:
@ -1487,7 +1520,7 @@ pq_fetch(cursorObject *curs, int no_result)
} }
else { else {
Dprintf("pq_fetch: got tuples, discarding them"); Dprintf("pq_fetch: got tuples, discarding them");
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
curs->rowcount = -1; curs->rowcount = -1;
ex = 0; ex = 0;
} }
@ -1496,14 +1529,13 @@ pq_fetch(cursorObject *curs, int no_result)
case PGRES_EMPTY_QUERY: case PGRES_EMPTY_QUERY:
PyErr_SetString(ProgrammingError, PyErr_SetString(ProgrammingError,
"can't execute an empty query"); "can't execute an empty query");
IFCLEARPGRES(curs->pgres); CLEARPGRES(curs->pgres);
ex = -1; ex = -1;
break; break;
default: default:
Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn); Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn);
pq_raise(curs->conn, curs, NULL); pq_raise(curs->conn, curs, NULL);
IFCLEARPGRES(curs->pgres);
ex = -1; ex = -1;
break; break;
} }

View File

@ -29,9 +29,8 @@
#include "psycopg/cursor.h" #include "psycopg/cursor.h"
#include "psycopg/connection.h" #include "psycopg/connection.h"
/* macros to clean the pg result */ /* macro to clean the pg result */
#define IFCLEARPGRES(pgres) if (pgres) {PQclear(pgres); pgres = NULL;} #define CLEARPGRES(pgres) do { PQclear(pgres); pgres = NULL; } while (0)
#define CLEARPGRES(pgres) PQclear(pgres); pgres = NULL
/* exported functions */ /* exported functions */
HIDDEN PGresult *pq_get_last_result(connectionObject *conn); HIDDEN PGresult *pq_get_last_result(connectionObject *conn);

View File

@ -116,14 +116,14 @@ typedef struct {
/* the Decimal type, used by the DECIMAL typecaster */ /* the Decimal type, used by the DECIMAL typecaster */
HIDDEN PyObject *psyco_GetDecimalType(void); HIDDEN PyObject *psyco_GetDecimalType(void);
/* forward declaration */ /* forward declarations */
typedef struct cursorObject cursorObject; typedef struct cursorObject cursorObject;
typedef struct connectionObject connectionObject;
/* some utility functions */ /* some utility functions */
RAISES HIDDEN void psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg, RAISES HIDDEN PyObject *psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg);
const char *pgerror, const char *pgcode);
HIDDEN char *psycopg_escape_string(PyObject *conn, HIDDEN char *psycopg_escape_string(connectionObject *conn,
const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen); const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen);
HIDDEN char *psycopg_escape_identifier_easy(const char *from, Py_ssize_t len); HIDDEN char *psycopg_escape_identifier_easy(const char *from, Py_ssize_t len);
HIDDEN int psycopg_strdup(char **to, const char *from, Py_ssize_t len); HIDDEN int psycopg_strdup(char **to, const char *from, Py_ssize_t len);

View File

@ -35,6 +35,8 @@
#include "psycopg/typecast.h" #include "psycopg/typecast.h"
#include "psycopg/microprotocols.h" #include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h" #include "psycopg/microprotocols_proto.h"
#include "psycopg/error.h"
#include "psycopg/diagnostics.h"
#include "psycopg/adapter_qstring.h" #include "psycopg/adapter_qstring.h"
#include "psycopg/adapter_binary.h" #include "psycopg/adapter_binary.h"
@ -175,8 +177,7 @@ psyco_register_type(PyObject *self, PyObject *args)
if (0 > typecast_add(type, NULL, 0)) { return NULL; } if (0 > typecast_add(type, NULL, 0)) { return NULL; }
} }
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
@ -408,8 +409,8 @@ static struct {
PyObject **base; PyObject **base;
const char *docstr; const char *docstr;
} exctable[] = { } exctable[] = {
{ "psycopg2.Error", &Error, 0, Error_doc }, { "psycopg2.Error", &Error, NULL, Error_doc },
{ "psycopg2.Warning", &Warning, 0, Warning_doc }, { "psycopg2.Warning", &Warning, NULL, Warning_doc },
{ "psycopg2.InterfaceError", &InterfaceError, &Error, InterfaceError_doc }, { "psycopg2.InterfaceError", &InterfaceError, &Error, InterfaceError_doc },
{ "psycopg2.DatabaseError", &DatabaseError, &Error, DatabaseError_doc }, { "psycopg2.DatabaseError", &DatabaseError, &Error, DatabaseError_doc },
{ "psycopg2.InternalError", &InternalError, &DatabaseError, InternalError_doc }, { "psycopg2.InternalError", &InternalError, &DatabaseError, InternalError_doc },
@ -433,61 +434,6 @@ static struct {
}; };
#if PY_VERSION_HEX >= 0x02050000
/* Error.__reduce_ex__
*
* The method is required to make exceptions picklable: set the cursor
* attribute to None. Only working from Py 2.5: previous versions
* would require implementing __getstate__, and as of 2012 it's a little
* bit too late to care. */
static PyObject *
psyco_error_reduce_ex(PyObject *self, PyObject *args)
{
PyObject *proto = NULL;
PyObject *super = NULL;
PyObject *tuple = NULL;
PyObject *dict = NULL;
PyObject *rv = NULL;
/* tuple = Exception.__reduce_ex__(self, proto) */
if (!PyArg_ParseTuple(args, "O", &proto)) {
goto error;
}
if (!(super = PyObject_GetAttrString(PyExc_Exception, "__reduce_ex__"))) {
goto error;
}
if (!(tuple = PyObject_CallFunctionObjArgs(super, self, proto, NULL))) {
goto error;
}
/* tuple[2]['cursor'] = None
*
* If these checks fail, we can still return a valid object. Pickle
* will likely fail downstream, but there's nothing else we can do here */
if (!PyTuple_Check(tuple)) { goto exit; }
if (3 > PyTuple_GET_SIZE(tuple)) { goto exit; }
dict = PyTuple_GET_ITEM(tuple, 2); /* borrowed */
if (!PyDict_Check(dict)) { goto exit; }
/* Modify the tuple inplace and return it */
if (0 != PyDict_SetItemString(dict, "cursor", Py_None)) {
goto error;
}
exit:
rv = tuple;
tuple = NULL;
error:
Py_XDECREF(tuple);
Py_XDECREF(super);
return rv;
}
#endif /* PY_VERSION_HEX >= 0x02050000 */
static int static int
psyco_errors_init(void) psyco_errors_init(void)
{ {
@ -497,17 +443,13 @@ psyco_errors_init(void)
int i; int i;
PyObject *dict = NULL; PyObject *dict = NULL;
PyObject *base;
PyObject *str = NULL; PyObject *str = NULL;
PyObject *descr = NULL;
int rv = -1; int rv = -1;
#if PY_VERSION_HEX >= 0x02050000 /* 'Error' has been defined elsewhere: only init the other classes */
static PyMethodDef psyco_error_reduce_ex_def = Error = (PyObject *)&errorType;
{"__reduce_ex__", psyco_error_reduce_ex, METH_VARARGS, "pickle helper"};
#endif
for (i=0; exctable[i].name; i++) { for (i = 1; exctable[i].name; i++) {
if (!(dict = PyDict_New())) { goto exit; } if (!(dict = PyDict_New())) { goto exit; }
if (exctable[i].docstr) { if (exctable[i].docstr) {
@ -516,51 +458,20 @@ psyco_errors_init(void)
Py_CLEAR(str); Py_CLEAR(str);
} }
if (exctable[i].base == 0) { /* can't put PyExc_StandardError in the static exctable:
#if PY_MAJOR_VERSION < 3 * windows build will fail */
base = PyExc_StandardError;
#else
/* StandardError is gone in 3.0 */
base = NULL;
#endif
}
else
base = *exctable[i].base;
if (!(*exctable[i].exc = PyErr_NewException( if (!(*exctable[i].exc = PyErr_NewException(
exctable[i].name, base, dict))) { exctable[i].name,
exctable[i].base ? *exctable[i].base : PyExc_StandardError,
dict))) {
goto exit; goto exit;
} }
Py_CLEAR(dict); Py_CLEAR(dict);
} }
/* Make pgerror, pgcode and cursor default to None on psycopg
error objects. This simplifies error handling code that checks
these attributes. */
PyObject_SetAttrString(Error, "pgerror", Py_None);
PyObject_SetAttrString(Error, "pgcode", Py_None);
PyObject_SetAttrString(Error, "cursor", Py_None);
/* install __reduce_ex__ on Error to make all the subclasses picklable.
*
* Don't install it on Py 2.4: it is not used by the pickle
* protocol, and if called manually fails in an unsettling way,
* probably because the exceptions were old-style classes. */
#if PY_VERSION_HEX >= 0x02050000
if (!(descr = PyDescr_NewMethod((PyTypeObject *)Error,
&psyco_error_reduce_ex_def))) {
goto exit;
}
if (0 != PyObject_SetAttrString(Error,
psyco_error_reduce_ex_def.ml_name, descr)) {
goto exit;
}
#endif
rv = 0; rv = 0;
exit: exit:
Py_XDECREF(descr);
Py_XDECREF(str); Py_XDECREF(str);
Py_XDECREF(dict); Py_XDECREF(dict);
return rv; return rv;
@ -604,11 +515,10 @@ psyco_errors_set(PyObject *type)
Create a new error of the given type with extra attributes. */ Create a new error of the given type with extra attributes. */
RAISES void /* TODO: may have been changed to BORROWED */
psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg, RAISES PyObject *
const char *pgerror, const char *pgcode) psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg)
{ {
PyObject *t;
PyObject *pymsg; PyObject *pymsg;
PyObject *err = NULL; PyObject *err = NULL;
connectionObject *conn = NULL; connectionObject *conn = NULL;
@ -624,31 +534,24 @@ psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
else { else {
/* what's better than an error in an error handler in the morning? /* what's better than an error in an error handler in the morning?
* Anyway, some error was set, refcount is ok... get outta here. */ * Anyway, some error was set, refcount is ok... get outta here. */
return; return NULL;
}
if (err && PyObject_TypeCheck(err, &errorType)) {
errorObject *perr = (errorObject *)err;
if (curs) {
Py_CLEAR(perr->cursor);
Py_INCREF(curs);
perr->cursor = curs;
}
} }
if (err) { if (err) {
if (curs) {
PyObject_SetAttrString(err, "cursor", (PyObject *)curs);
}
if (pgerror) {
if ((t = conn_text_from_chars(conn, pgerror))) {
PyObject_SetAttrString(err, "pgerror", t);
Py_DECREF(t);
}
}
if (pgcode) {
if ((t = conn_text_from_chars(conn, pgcode))) {
PyObject_SetAttrString(err, "pgcode", t);
Py_DECREF(t);
}
}
PyErr_SetObject(exc, err); PyErr_SetObject(exc, err);
Py_DECREF(err); Py_DECREF(err);
} }
return err;
} }
@ -868,39 +771,59 @@ INIT_MODULE(_psycopg)(void)
/* initialize all the new types and then the module */ /* initialize all the new types and then the module */
Py_TYPE(&connectionType) = &PyType_Type; Py_TYPE(&connectionType) = &PyType_Type;
Py_TYPE(&cursorType) = &PyType_Type;
Py_TYPE(&typecastType) = &PyType_Type;
Py_TYPE(&qstringType) = &PyType_Type;
Py_TYPE(&binaryType) = &PyType_Type;
Py_TYPE(&isqlquoteType) = &PyType_Type;
Py_TYPE(&pbooleanType) = &PyType_Type;
Py_TYPE(&pintType) = &PyType_Type;
Py_TYPE(&pfloatType) = &PyType_Type;
Py_TYPE(&pdecimalType) = &PyType_Type;
Py_TYPE(&asisType) = &PyType_Type;
Py_TYPE(&listType) = &PyType_Type;
Py_TYPE(&chunkType) = &PyType_Type;
Py_TYPE(&NotifyType) = &PyType_Type;
Py_TYPE(&XidType) = &PyType_Type;
if (PyType_Ready(&connectionType) == -1) goto exit; if (PyType_Ready(&connectionType) == -1) goto exit;
Py_TYPE(&cursorType) = &PyType_Type;
if (PyType_Ready(&cursorType) == -1) goto exit; if (PyType_Ready(&cursorType) == -1) goto exit;
Py_TYPE(&typecastType) = &PyType_Type;
if (PyType_Ready(&typecastType) == -1) goto exit; if (PyType_Ready(&typecastType) == -1) goto exit;
Py_TYPE(&qstringType) = &PyType_Type;
if (PyType_Ready(&qstringType) == -1) goto exit; if (PyType_Ready(&qstringType) == -1) goto exit;
Py_TYPE(&binaryType) = &PyType_Type;
if (PyType_Ready(&binaryType) == -1) goto exit; if (PyType_Ready(&binaryType) == -1) goto exit;
Py_TYPE(&isqlquoteType) = &PyType_Type;
if (PyType_Ready(&isqlquoteType) == -1) goto exit; if (PyType_Ready(&isqlquoteType) == -1) goto exit;
Py_TYPE(&pbooleanType) = &PyType_Type;
if (PyType_Ready(&pbooleanType) == -1) goto exit; if (PyType_Ready(&pbooleanType) == -1) goto exit;
Py_TYPE(&pintType) = &PyType_Type;
if (PyType_Ready(&pintType) == -1) goto exit; if (PyType_Ready(&pintType) == -1) goto exit;
Py_TYPE(&pfloatType) = &PyType_Type;
if (PyType_Ready(&pfloatType) == -1) goto exit; if (PyType_Ready(&pfloatType) == -1) goto exit;
Py_TYPE(&pdecimalType) = &PyType_Type;
if (PyType_Ready(&pdecimalType) == -1) goto exit; if (PyType_Ready(&pdecimalType) == -1) goto exit;
Py_TYPE(&asisType) = &PyType_Type;
if (PyType_Ready(&asisType) == -1) goto exit; if (PyType_Ready(&asisType) == -1) goto exit;
Py_TYPE(&listType) = &PyType_Type;
if (PyType_Ready(&listType) == -1) goto exit; if (PyType_Ready(&listType) == -1) goto exit;
Py_TYPE(&chunkType) = &PyType_Type;
if (PyType_Ready(&chunkType) == -1) goto exit; if (PyType_Ready(&chunkType) == -1) goto exit;
if (PyType_Ready(&NotifyType) == -1) goto exit;
if (PyType_Ready(&XidType) == -1) goto exit; Py_TYPE(&notifyType) = &PyType_Type;
if (PyType_Ready(&notifyType) == -1) goto exit;
Py_TYPE(&xidType) = &PyType_Type;
if (PyType_Ready(&xidType) == -1) goto exit;
Py_TYPE(&errorType) = &PyType_Type;
errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
if (PyType_Ready(&errorType) == -1) goto exit;
Py_TYPE(&diagnosticsType) = &PyType_Type;
if (PyType_Ready(&diagnosticsType) == -1) goto exit;
#ifdef PSYCOPG_EXTENSIONS #ifdef PSYCOPG_EXTENSIONS
Py_TYPE(&lobjectType) = &PyType_Type; Py_TYPE(&lobjectType) = &PyType_Type;
if (PyType_Ready(&lobjectType) == -1) goto exit; if (PyType_Ready(&lobjectType) == -1) goto exit;
#endif #endif
@ -908,6 +831,7 @@ INIT_MODULE(_psycopg)(void)
#ifdef HAVE_MXDATETIME #ifdef HAVE_MXDATETIME
Py_TYPE(&mxdatetimeType) = &PyType_Type; Py_TYPE(&mxdatetimeType) = &PyType_Type;
if (PyType_Ready(&mxdatetimeType) == -1) goto exit; if (PyType_Ready(&mxdatetimeType) == -1) goto exit;
if (0 != mxDateTime_ImportModuleAndAPI()) { if (0 != mxDateTime_ImportModuleAndAPI()) {
PyErr_Clear(); PyErr_Clear();
@ -985,8 +909,9 @@ INIT_MODULE(_psycopg)(void)
PyModule_AddObject(module, "connection", (PyObject*)&connectionType); PyModule_AddObject(module, "connection", (PyObject*)&connectionType);
PyModule_AddObject(module, "cursor", (PyObject*)&cursorType); PyModule_AddObject(module, "cursor", (PyObject*)&cursorType);
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType); PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
PyModule_AddObject(module, "Notify", (PyObject*)&NotifyType); PyModule_AddObject(module, "Notify", (PyObject*)&notifyType);
PyModule_AddObject(module, "Xid", (PyObject*)&XidType); PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
#ifdef PSYCOPG_EXTENSIONS #ifdef PSYCOPG_EXTENSIONS
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType); PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
#endif #endif
@ -1015,31 +940,6 @@ INIT_MODULE(_psycopg)(void)
if (0 != psyco_errors_init()) { goto exit; } if (0 != psyco_errors_init()) { goto exit; }
psyco_errors_fill(dict); psyco_errors_fill(dict);
/* Solve win32 build issue about non-constant initializer element */
cursorType.tp_alloc = PyType_GenericAlloc;
binaryType.tp_alloc = PyType_GenericAlloc;
isqlquoteType.tp_alloc = PyType_GenericAlloc;
pbooleanType.tp_alloc = PyType_GenericAlloc;
pintType.tp_alloc = PyType_GenericAlloc;
pfloatType.tp_alloc = PyType_GenericAlloc;
pdecimalType.tp_alloc = PyType_GenericAlloc;
connectionType.tp_alloc = PyType_GenericAlloc;
asisType.tp_alloc = PyType_GenericAlloc;
qstringType.tp_alloc = PyType_GenericAlloc;
listType.tp_alloc = PyType_GenericAlloc;
chunkType.tp_alloc = PyType_GenericAlloc;
pydatetimeType.tp_alloc = PyType_GenericAlloc;
NotifyType.tp_alloc = PyType_GenericAlloc;
XidType.tp_alloc = PyType_GenericAlloc;
#ifdef PSYCOPG_EXTENSIONS
lobjectType.tp_alloc = PyType_GenericAlloc;
#endif
#ifdef HAVE_MXDATETIME
mxdatetimeType.tp_alloc = PyType_GenericAlloc;
#endif
Dprintf("initpsycopg: module initialization complete"); Dprintf("initpsycopg: module initialization complete");
exit: exit:

View File

@ -31,36 +31,12 @@
#include <stringobject.h> #include <stringobject.h>
#endif #endif
#if PY_VERSION_HEX < 0x02040000
# error "psycopg requires Python >= 2.4"
#endif
#if PY_VERSION_HEX < 0x02050000 #if PY_VERSION_HEX < 0x02050000
/* Function missing in Py 2.4 */ # error "psycopg requires Python >= 2.5"
#define PyErr_WarnEx(cat,msg,lvl) PyErr_Warn(cat,msg)
#endif
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
typedef int Py_ssize_t;
#define PY_SSIZE_T_MIN INT_MIN
#define PY_SSIZE_T_MAX INT_MAX
#define PY_FORMAT_SIZE_T ""
#define PyInt_FromSsize_t(x) PyInt_FromLong((x))
#define lenfunc inquiry
#define ssizeargfunc intargfunc
#define readbufferproc getreadbufferproc
#define writebufferproc getwritebufferproc
#define segcountproc getsegcountproc
#define charbufferproc getcharbufferproc
#define CONV_CODE_PY_SSIZE_T "i"
#else
#define CONV_CODE_PY_SSIZE_T "n"
#endif #endif
/* hash() return size changed around version 3.2a4 on 64bit platforms. Before /* hash() return size changed around version 3.2a4 on 64bit platforms. Before
* this, the return size was always a long, regardless of arch. ~3.2 * this, the return size was always a long, regardless of arch. ~3.2
* introduced the Py_hash_t & Py_uhash_t typedefs with the resulting sizes * introduced the Py_hash_t & Py_uhash_t typedefs with the resulting sizes
* based upon arch. */ * based upon arch. */
#if PY_VERSION_HEX < 0x030200A4 #if PY_VERSION_HEX < 0x030200A4
@ -76,11 +52,6 @@ typedef unsigned long Py_uhash_t;
#define PyVarObject_HEAD_INIT(x,n) PyObject_HEAD_INIT(x) n, #define PyVarObject_HEAD_INIT(x,n) PyObject_HEAD_INIT(x) n,
#endif #endif
/* Missing at least in Python 2.4 */
#ifndef Py_MEMCPY
#define Py_MEMCPY memcpy
#endif
/* FORMAT_CODE_PY_SSIZE_T is for Py_ssize_t: */ /* FORMAT_CODE_PY_SSIZE_T is for Py_ssize_t: */
#define FORMAT_CODE_PY_SSIZE_T "%" PY_FORMAT_SIZE_T "d" #define FORMAT_CODE_PY_SSIZE_T "%" PY_FORMAT_SIZE_T "d"
@ -114,6 +85,7 @@ typedef unsigned long Py_uhash_t;
#define PyInt_AsLong PyLong_AsLong #define PyInt_AsLong PyLong_AsLong
#define PyInt_FromLong PyLong_FromLong #define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSsize_t PyLong_FromSsize_t #define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyExc_StandardError PyExc_Exception
#define PyString_FromFormat PyUnicode_FromFormat #define PyString_FromFormat PyUnicode_FromFormat
#define Py_TPFLAGS_HAVE_ITER 0L #define Py_TPFLAGS_HAVE_ITER 0L
#define Py_TPFLAGS_HAVE_RICHCOMPARE 0L #define Py_TPFLAGS_HAVE_RICHCOMPARE 0L

View File

@ -414,26 +414,27 @@ static struct PyMemberDef typecastObject_members[] = {
{NULL} {NULL}
}; };
static void static int
typecast_dealloc(PyObject *obj) typecast_clear(typecastObject *self)
{ {
typecastObject *self = (typecastObject*)obj;
PyObject_GC_UnTrack(self);
Py_CLEAR(self->values); Py_CLEAR(self->values);
Py_CLEAR(self->name); Py_CLEAR(self->name);
Py_CLEAR(self->pcast); Py_CLEAR(self->pcast);
Py_CLEAR(self->bcast); Py_CLEAR(self->bcast);
return 0;
}
Py_TYPE(obj)->tp_free(obj); static void
typecast_dealloc(typecastObject *self)
{
PyObject_GC_UnTrack(self);
typecast_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
} }
static int static int
typecast_traverse(PyObject *obj, visitproc visit, void *arg) typecast_traverse(typecastObject *self, visitproc visit, void *arg)
{ {
typecastObject *self = (typecastObject*)obj;
Py_VISIT(self->values); Py_VISIT(self->values);
Py_VISIT(self->name); Py_VISIT(self->name);
Py_VISIT(self->pcast); Py_VISIT(self->pcast);
@ -441,12 +442,6 @@ typecast_traverse(PyObject *obj, visitproc visit, void *arg)
return 0; return 0;
} }
static void
typecast_del(void *self)
{
PyObject_GC_Del(self);
}
static PyObject * static PyObject *
typecast_repr(PyObject *self) typecast_repr(PyObject *self)
{ {
@ -479,8 +474,7 @@ typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs)
// If the string is not a string but a None value we're being called // If the string is not a string but a None value we're being called
// from a Python-defined caster. // from a Python-defined caster.
if (!string) { if (!string) {
Py_INCREF(Py_None); Py_RETURN_NONE;
return Py_None;
} }
return typecast_cast(obj, string, length, cursor); return typecast_cast(obj, string, length, cursor);
@ -489,10 +483,8 @@ typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs)
PyTypeObject typecastType = { PyTypeObject typecastType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.type", "psycopg2._psycopg.type",
sizeof(typecastObject), sizeof(typecastObject), 0,
0, (destructor)typecast_dealloc, /*tp_dealloc*/
typecast_dealloc, /*tp_dealloc*/
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
@ -502,48 +494,31 @@ PyTypeObject typecastType = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
typecast_call, /*tp_call*/ typecast_call, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_RICHCOMPARE | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_RICHCOMPARE |
Py_TPFLAGS_HAVE_GC, /*tp_flags*/ Py_TPFLAGS_HAVE_GC, /*tp_flags*/
"psycopg type-casting object", /*tp_doc*/ "psycopg type-casting object", /*tp_doc*/
(traverseproc)typecast_traverse, /*tp_traverse*/
typecast_traverse, /*tp_traverse*/ (inquiry)typecast_clear, /*tp_clear*/
0, /*tp_clear*/
typecast_richcompare, /*tp_richcompare*/ typecast_richcompare, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
0, /*tp_methods*/
/* Attribute descriptor and subclassing stuff */
0, /*tp_methods*/
typecastObject_members, /*tp_members*/ typecastObject_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
0, /*tp_init*/
0, /*tp_init*/ 0, /*tp_alloc*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_new*/
0, /*tp_new*/
typecast_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*/
}; };
static PyObject * static PyObject *

View File

@ -253,7 +253,7 @@ typecast_GENERIC_ARRAY_cast(const char *str, Py_ssize_t len, PyObject *curs)
Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s'," Dprintf("typecast_GENERIC_ARRAY_cast: str = '%s',"
" len = " FORMAT_CODE_PY_SSIZE_T, str, len); " len = " FORMAT_CODE_PY_SSIZE_T, str, len);
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
if (str[0] == '[') if (str[0] == '[')
typecast_array_cleanup(&str, &len); typecast_array_cleanup(&str, &len);
if (str[0] != '{') { if (str[0] != '{') {

View File

@ -31,7 +31,7 @@ typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
char buffer[12]; char buffer[12];
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if (s[len] != '\0') { if (s[len] != '\0') {
strncpy(buffer, s, (size_t) len); buffer[len] = '\0'; strncpy(buffer, s, (size_t) len); buffer[len] = '\0';
s = buffer; s = buffer;
@ -49,7 +49,7 @@ typecast_LONGINTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
char buffer[24]; char buffer[24];
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if (s[len] != '\0') { if (s[len] != '\0') {
strncpy(buffer, s, (size_t) len); buffer[len] = '\0'; strncpy(buffer, s, (size_t) len); buffer[len] = '\0';
s = buffer; s = buffer;
@ -64,7 +64,7 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
PyObject *str = NULL, *flo = NULL; PyObject *str = NULL, *flo = NULL;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if (!(str = Text_FromUTF8AndSize(s, len))) { return NULL; } if (!(str = Text_FromUTF8AndSize(s, len))) { return NULL; }
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
flo = PyFloat_FromString(str, NULL); flo = PyFloat_FromString(str, NULL);
@ -81,7 +81,7 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs)
static PyObject * static PyObject *
typecast_STRING_cast(const char *s, Py_ssize_t len, PyObject *curs) typecast_STRING_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
return PyString_FromStringAndSize(s, len); return PyString_FromStringAndSize(s, len);
} }
#else #else
@ -95,7 +95,7 @@ typecast_UNICODE_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
char *enc; char *enc;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
enc = ((cursorObject*)curs)->conn->codec; enc = ((cursorObject*)curs)->conn->codec;
return PyUnicode_Decode(s, len, enc, NULL); return PyUnicode_Decode(s, len, enc, NULL);
@ -108,7 +108,7 @@ typecast_BOOLEAN_cast(const char *s, Py_ssize_t len, PyObject *curs)
{ {
PyObject *res; PyObject *res;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if (s[0] == 't') if (s[0] == 't')
res = Py_True; res = Py_True;
@ -128,7 +128,7 @@ typecast_DECIMAL_cast(const char *s, Py_ssize_t len, PyObject *curs)
PyObject *decimalType; PyObject *decimalType;
char *buffer; char *buffer;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if ((buffer = PyMem_Malloc(len+1)) == NULL) if ((buffer = PyMem_Malloc(len+1)) == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();

View File

@ -55,7 +55,6 @@ chunk_repr(chunkObject *self)
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
/* XXX support 3.0 buffer protocol */
static Py_ssize_t static Py_ssize_t
chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr) chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr)
{ {
@ -90,9 +89,15 @@ static PyBufferProcs chunk_as_buffer =
/* 3.0 buffer interface */ /* 3.0 buffer interface */
int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags) int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags)
{ {
int rv;
chunkObject *self = (chunkObject*)_self; chunkObject *self = (chunkObject*)_self;
return PyBuffer_FillInfo(view, _self, self->base, self->len, 1, flags); rv = PyBuffer_FillInfo(view, _self, self->base, self->len, 1, flags);
if (rv == 0) {
view->format = "c";
}
return rv;
} }
static PyBufferProcs chunk_as_buffer = static PyBufferProcs chunk_as_buffer =
{ {
chunk_getbuffer, chunk_getbuffer,
@ -105,9 +110,8 @@ static PyBufferProcs chunk_as_buffer =
PyTypeObject chunkType = { PyTypeObject chunkType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.chunk", /* tp_name */ "psycopg2._psycopg.chunk",
sizeof(chunkObject), /* tp_basicsize */ sizeof(chunkObject), 0,
0, /* tp_itemsize */
(destructor) chunk_dealloc, /* tp_dealloc*/ (destructor) chunk_dealloc, /* tp_dealloc*/
0, /* tp_print */ 0, /* tp_print */
0, /* tp_getattr */ 0, /* tp_getattr */
@ -142,7 +146,7 @@ typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs)
char *buffer = NULL; char *buffer = NULL;
Py_ssize_t len; Py_ssize_t len;
if (s == NULL) {Py_INCREF(Py_None); return Py_None;} if (s == NULL) { Py_RETURN_NONE; }
if (s[0] == '\\' && s[1] == 'x') { if (s[0] == '\\' && s[1] == 'x') {
/* This is a buffer escaped in hex format: libpq before 9.0 can't /* This is a buffer escaped in hex format: libpq before 9.0 can't

View File

@ -48,7 +48,7 @@ typecast_PYDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
PyObject* obj = NULL; PyObject* obj = NULL;
int n, y=0, m=0, d=0; int n, y=0, m=0, d=0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') { if (str[0] == '-') {
@ -92,7 +92,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
int hh=0, mm=0, ss=0, us=0, tz=0; int hh=0, mm=0, ss=0, us=0, tz=0;
const char *tp = NULL; const char *tp = NULL;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
/* check for infinity */ /* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) { if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
@ -177,7 +177,7 @@ typecast_PYTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
PyObject *tzinfo_factory; PyObject *tzinfo_factory;
int n, hh=0, mm=0, ss=0, us=0, tz=0; int n, hh=0, mm=0, ss=0, us=0, tz=0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz); n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_PYTIME_cast: n = %d, len = " FORMAT_CODE_PY_SSIZE_T ", " Dprintf("typecast_PYTIME_cast: n = %d, len = " FORMAT_CODE_PY_SSIZE_T ", "
@ -226,7 +226,7 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
int part = 0, sec; int part = 0, sec;
double micro; double micro;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_PYINTERVAL_cast: s = %s", str); Dprintf("typecast_PYINTERVAL_cast: s = %s", str);

View File

@ -51,7 +51,7 @@ typecast_MXDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
int hh=0, mm=0, ss=0, us=0, tz=0; int hh=0, mm=0, ss=0, us=0, tz=0;
const char *tp = NULL; const char *tp = NULL;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXDATE_cast: s = %s", str); Dprintf("typecast_MXDATE_cast: s = %s", str);
@ -99,7 +99,7 @@ typecast_MXTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
{ {
int n, hh=0, mm=0, ss=0, us=0, tz=0; int n, hh=0, mm=0, ss=0, us=0, tz=0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXTIME_cast: s = %s", str); Dprintf("typecast_MXTIME_cast: s = %s", str);
@ -129,7 +129,7 @@ typecast_MXINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
double v = 0.0, sign = 1.0; double v = 0.0, sign = 1.0;
int part = 0; int part = 0;
if (str == NULL) {Py_INCREF(Py_None); return Py_None;} if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXINTERVAL_cast: s = %s", str); Dprintf("typecast_MXINTERVAL_cast: s = %s", str);

View File

@ -32,21 +32,33 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
/* Escape a string for sql inclusion.
*
* The function must be called holding the GIL.
*
* Return a pointer to a new string on the Python heap on success, else NULL
* and set an exception. The returned string includes quotes and leading E if
* needed.
*
* If tolen is set, it will contain the length of the escaped string,
* including quotes.
*/
char * char *
psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len, psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
char *to, Py_ssize_t *tolen) char *to, Py_ssize_t *tolen)
{ {
Py_ssize_t ql; Py_ssize_t ql;
connectionObject *conn = (connectionObject*)obj; int eq = (conn && (conn->equote)) ? 1 : 0;
int eq = (conn && (conn->equote)) ? 1 : 0;
if (len == 0) if (len == 0)
len = strlen(from); len = strlen(from);
if (to == NULL) { if (to == NULL) {
to = (char *)PyMem_Malloc((len * 2 + 4) * sizeof(char)); to = (char *)PyMem_Malloc((len * 2 + 4) * sizeof(char));
if (to == NULL) if (to == NULL) {
PyErr_NoMemory();
return NULL; return NULL;
}
} }
{ {
@ -59,15 +71,19 @@ psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len,
ql = PQescapeString(to+eq+1, from, len); ql = PQescapeString(to+eq+1, from, len);
} }
if (eq) if (eq) {
to[0] = 'E'; to[0] = 'E';
to[eq] = '\''; to[1] = to[ql+2] = '\'';
to[ql+eq+1] = '\''; to[ql+3] = '\0';
to[ql+eq+2] = '\0'; }
else {
to[0] = to[ql+1] = '\'';
to[ql+2] = '\0';
}
if (tolen) if (tolen)
*tolen = ql+eq+2; *tolen = ql+eq+2;
return to; return to;
} }
@ -115,10 +131,16 @@ psycopg_escape_identifier_easy(const char *from, Py_ssize_t len)
* *
* Store the return in 'to' and return 0 in case of success, else return -1 * Store the return in 'to' and return 0 in case of success, else return -1
* and raise an exception. * and raise an exception.
*
* If from is null, store null into to.
*/ */
RAISES_NEG int RAISES_NEG int
psycopg_strdup(char **to, const char *from, Py_ssize_t len) psycopg_strdup(char **to, const char *from, Py_ssize_t len)
{ {
if (!from) {
*to = NULL;
return 0;
}
if (!len) { len = strlen(from); } if (!len) { len = strlen(from); }
if (!(*to = PyMem_Malloc(len + 1))) { if (!(*to = PyMem_Malloc(len + 1))) {
PyErr_NoMemory(); PyErr_NoMemory();

View File

@ -27,7 +27,7 @@
#ifndef PSYCOPG_XID_H #ifndef PSYCOPG_XID_H
#define PSYCOPG_XID_H 1 #define PSYCOPG_XID_H 1
extern HIDDEN PyTypeObject XidType; extern HIDDEN PyTypeObject xidType;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -41,11 +41,11 @@ typedef struct {
PyObject *prepared; PyObject *prepared;
PyObject *owner; PyObject *owner;
PyObject *database; PyObject *database;
} XidObject; } xidObject;
HIDDEN XidObject *xid_ensure(PyObject *oxid); HIDDEN xidObject *xid_ensure(PyObject *oxid);
HIDDEN XidObject *xid_from_string(PyObject *s); HIDDEN xidObject *xid_from_string(PyObject *s);
HIDDEN PyObject *xid_get_tid(XidObject *self); HIDDEN PyObject *xid_get_tid(xidObject *self);
HIDDEN PyObject *xid_recover(PyObject *conn); HIDDEN PyObject *xid_recover(PyObject *conn);
#endif /* PSYCOPG_XID_H */ #endif /* PSYCOPG_XID_H */

View File

@ -67,46 +67,28 @@ static const char database_doc[] =
"Database the recovered transaction belongs to."; "Database the recovered transaction belongs to.";
static PyMemberDef xid_members[] = { static PyMemberDef xid_members[] = {
{ "format_id", T_OBJECT, offsetof(XidObject, format_id), READONLY, (char *)format_id_doc }, { "format_id", T_OBJECT, offsetof(xidObject, format_id), READONLY, (char *)format_id_doc },
{ "gtrid", T_OBJECT, offsetof(XidObject, gtrid), READONLY, (char *)gtrid_doc }, { "gtrid", T_OBJECT, offsetof(xidObject, gtrid), READONLY, (char *)gtrid_doc },
{ "bqual", T_OBJECT, offsetof(XidObject, bqual), READONLY, (char *)bqual_doc }, { "bqual", T_OBJECT, offsetof(xidObject, bqual), READONLY, (char *)bqual_doc },
{ "prepared", T_OBJECT, offsetof(XidObject, prepared), READONLY, (char *)prepared_doc }, { "prepared", T_OBJECT, offsetof(xidObject, prepared), READONLY, (char *)prepared_doc },
{ "owner", T_OBJECT, offsetof(XidObject, owner), READONLY, (char *)owner_doc }, { "owner", T_OBJECT, offsetof(xidObject, owner), READONLY, (char *)owner_doc },
{ "database", T_OBJECT, offsetof(XidObject, database), READONLY, (char *)database_doc }, { "database", T_OBJECT, offsetof(xidObject, database), READONLY, (char *)database_doc },
{ NULL } { NULL }
}; };
static PyObject * static PyObject *
xid_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) xid_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{ {
XidObject *self; return type->tp_alloc(type, 0);
if (!(self = (XidObject *)type->tp_alloc(type, 0))) { return NULL; }
Py_INCREF(Py_None);
self->format_id = Py_None;
Py_INCREF(Py_None);
self->gtrid = Py_None;
Py_INCREF(Py_None);
self->bqual = Py_None;
Py_INCREF(Py_None);
self->prepared = Py_None;
Py_INCREF(Py_None);
self->owner = Py_None;
Py_INCREF(Py_None);
self->database = Py_None;
return (PyObject *)self;
} }
static int static int
xid_init(XidObject *self, PyObject *args, PyObject *kwargs) xid_init(xidObject *self, PyObject *args, PyObject *kwargs)
{ {
static char *kwlist[] = {"format_id", "gtrid", "bqual", NULL}; static char *kwlist[] = {"format_id", "gtrid", "bqual", NULL};
int format_id; int format_id;
size_t i, gtrid_len, bqual_len; size_t i, gtrid_len, bqual_len;
const char *gtrid, *bqual; const char *gtrid, *bqual;
PyObject *tmp;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iss", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iss", kwlist,
&format_id, &gtrid, &bqual)) &format_id, &gtrid, &bqual))
@ -149,35 +131,18 @@ xid_init(XidObject *self, PyObject *args, PyObject *kwargs)
} }
} }
tmp = self->format_id; if (!(self->format_id = PyInt_FromLong(format_id))) { return -1; }
self->format_id = PyInt_FromLong(format_id); if (!(self->gtrid = Text_FromUTF8(gtrid))) { return -1; }
Py_XDECREF(tmp); if (!(self->bqual = Text_FromUTF8(bqual))) { return -1; }
Py_INCREF(Py_None); self->prepared = Py_None;
Py_INCREF(Py_None); self->owner = Py_None;
Py_INCREF(Py_None); self->database = Py_None;
tmp = self->gtrid;
self->gtrid = Text_FromUTF8(gtrid);
Py_XDECREF(tmp);
tmp = self->bqual;
self->bqual = Text_FromUTF8(bqual);
Py_XDECREF(tmp);
return 0;
}
static int
xid_traverse(XidObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->format_id);
Py_VISIT(self->gtrid);
Py_VISIT(self->bqual);
Py_VISIT(self->prepared);
Py_VISIT(self->owner);
Py_VISIT(self->database);
return 0; return 0;
} }
static void static void
xid_dealloc(XidObject *self) xid_dealloc(xidObject *self)
{ {
Py_CLEAR(self->format_id); Py_CLEAR(self->format_id);
Py_CLEAR(self->gtrid); Py_CLEAR(self->gtrid);
@ -189,20 +154,14 @@ xid_dealloc(XidObject *self)
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free((PyObject *)self);
} }
static void
xid_del(PyObject *self)
{
PyObject_GC_Del(self);
}
static Py_ssize_t static Py_ssize_t
xid_len(XidObject *self) xid_len(xidObject *self)
{ {
return 3; return 3;
} }
static PyObject * static PyObject *
xid_getitem(XidObject *self, Py_ssize_t item) xid_getitem(xidObject *self, Py_ssize_t item)
{ {
if (item < 0) if (item < 0)
item += 3; item += 3;
@ -224,13 +183,13 @@ xid_getitem(XidObject *self, Py_ssize_t item)
} }
static PyObject * static PyObject *
xid_str(XidObject *self) xid_str(xidObject *self)
{ {
return xid_get_tid(self); return xid_get_tid(self);
} }
static PyObject * static PyObject *
xid_repr(XidObject *self) xid_repr(xidObject *self)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *format = NULL; PyObject *format = NULL;
@ -305,66 +264,45 @@ static struct PyMethodDef xid_methods[] = {
{NULL} {NULL}
}; };
PyTypeObject XidType = { PyTypeObject xidType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Xid", "psycopg2.extensions.Xid",
sizeof(XidObject), sizeof(xidObject), 0,
0,
(destructor)xid_dealloc, /* tp_dealloc */ (destructor)xid_dealloc, /* tp_dealloc */
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
0, /*tp_compare*/ 0, /*tp_compare*/
(reprfunc)xid_repr, /*tp_repr*/ (reprfunc)xid_repr, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
&xid_sequence, /*tp_as_sequence*/ &xid_sequence, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash */ 0, /*tp_hash */
0, /*tp_call*/ 0, /*tp_call*/
(reprfunc)xid_str, /*tp_str*/ (reprfunc)xid_str, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
/* Notify is not GC as it only has string attributes */
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
xid_doc, /*tp_doc*/ xid_doc, /*tp_doc*/
0, /*tp_traverse*/
(traverseproc)xid_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
xid_methods, /*tp_methods*/ xid_methods, /*tp_methods*/
xid_members, /*tp_members*/ xid_members, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
0, /*tp_descr_set*/ 0, /*tp_descr_set*/
0, /*tp_dictoffset*/ 0, /*tp_dictoffset*/
(initproc)xid_init, /*tp_init*/ (initproc)xid_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ 0, /*tp_alloc*/
xid_new, /*tp_new*/ xid_new, /*tp_new*/
(freefunc)xid_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*/
}; };
@ -376,13 +314,13 @@ PyTypeObject XidType = {
* or use a regular string they have found in PostgreSQL's pg_prepared_xacts * or use a regular string they have found in PostgreSQL's pg_prepared_xacts
* in order to recover a transaction not generated by psycopg. * in order to recover a transaction not generated by psycopg.
*/ */
XidObject *xid_ensure(PyObject *oxid) xidObject *xid_ensure(PyObject *oxid)
{ {
XidObject *rv = NULL; xidObject *rv = NULL;
if (PyObject_TypeCheck(oxid, &XidType)) { if (PyObject_TypeCheck(oxid, &xidType)) {
Py_INCREF(oxid); Py_INCREF(oxid);
rv = (XidObject *)oxid; rv = (xidObject *)oxid;
} }
else { else {
rv = xid_from_string(oxid); rv = xid_from_string(oxid);
@ -445,7 +383,7 @@ _xid_decode64(PyObject *s)
* http://cvs.pgfoundry.org/cgi-bin/cvsweb.cgi/jdbc/pgjdbc/org/postgresql/xa/RecoveredXid.java?rev=1.2 * http://cvs.pgfoundry.org/cgi-bin/cvsweb.cgi/jdbc/pgjdbc/org/postgresql/xa/RecoveredXid.java?rev=1.2
*/ */
PyObject * PyObject *
xid_get_tid(XidObject *self) xid_get_tid(xidObject *self)
{ {
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *egtrid = NULL; PyObject *egtrid = NULL;
@ -525,7 +463,7 @@ exit:
* *
* Return NULL + exception if parsing failed. Else a new Xid object. */ * Return NULL + exception if parsing failed. Else a new Xid object. */
static XidObject * static xidObject *
_xid_parse_string(PyObject *str) { _xid_parse_string(PyObject *str) {
PyObject *regex; PyObject *regex;
PyObject *m = NULL; PyObject *m = NULL;
@ -536,7 +474,7 @@ _xid_parse_string(PyObject *str) {
PyObject *ebqual = NULL; PyObject *ebqual = NULL;
PyObject *gtrid = NULL; PyObject *gtrid = NULL;
PyObject *bqual = NULL; PyObject *bqual = NULL;
XidObject *rv = NULL; xidObject *rv = NULL;
/* check if the string is a possible XA triple with a regexp */ /* check if the string is a possible XA triple with a regexp */
if (!(regex = _xid_get_parse_regex())) { goto exit; } if (!(regex = _xid_get_parse_regex())) { goto exit; }
@ -560,7 +498,7 @@ _xid_parse_string(PyObject *str) {
if (!(bqual = _xid_decode64(ebqual))) { goto exit; } if (!(bqual = _xid_decode64(ebqual))) { goto exit; }
/* Try to build the xid with the parsed material */ /* Try to build the xid with the parsed material */
rv = (XidObject *)PyObject_CallFunctionObjArgs((PyObject *)&XidType, rv = (xidObject *)PyObject_CallFunctionObjArgs((PyObject *)&xidType,
format_id, gtrid, bqual, NULL); format_id, gtrid, bqual, NULL);
exit: exit:
@ -579,35 +517,31 @@ exit:
/* Return a new Xid object representing a transaction ID not conform to /* Return a new Xid object representing a transaction ID not conform to
* the XA specifications. */ * the XA specifications. */
static XidObject * static xidObject *
_xid_unparsed_from_string(PyObject *str) { _xid_unparsed_from_string(PyObject *str) {
XidObject *xid = NULL; xidObject *xid = NULL;
XidObject *rv = NULL; xidObject *rv = NULL;
PyObject *tmp;
/* fake args to work around the checks performed by the xid init */ /* fake args to work around the checks performed by the xid init */
if (!(xid = (XidObject *)PyObject_CallFunction((PyObject *)&XidType, if (!(xid = (xidObject *)PyObject_CallFunction((PyObject *)&xidType,
"iss", 0, "", ""))) { "iss", 0, "", ""))) {
goto exit; goto exit;
} }
/* set xid.gtrid = str */ /* set xid.gtrid = str */
tmp = xid->gtrid; Py_CLEAR(xid->gtrid);
Py_INCREF(str); Py_INCREF(str);
xid->gtrid = str; xid->gtrid = str;
Py_DECREF(tmp);
/* set xid.format_id = None */ /* set xid.format_id = None */
tmp = xid->format_id; Py_CLEAR(xid->format_id);
Py_INCREF(Py_None); Py_INCREF(Py_None);
xid->format_id = Py_None; xid->format_id = Py_None;
Py_DECREF(tmp);
/* set xid.bqual = None */ /* set xid.bqual = None */
tmp = xid->bqual; Py_CLEAR(xid->bqual);
Py_INCREF(Py_None); Py_INCREF(Py_None);
xid->bqual = Py_None; xid->bqual = Py_None;
Py_DECREF(tmp);
/* return the finished object */ /* return the finished object */
rv = xid; rv = xid;
@ -624,9 +558,9 @@ exit:
* If the xid is in the format generated by Psycopg, unpack the tuple into * If the xid is in the format generated by Psycopg, unpack the tuple into
* the struct members. Otherwise generate an "unparsed" xid. * the struct members. Otherwise generate an "unparsed" xid.
*/ */
XidObject * xidObject *
xid_from_string(PyObject *str) { xid_from_string(PyObject *str) {
XidObject *rv; xidObject *rv;
if (!(Bytes_Check(str) || PyUnicode_Check(str))) { if (!(Bytes_Check(str) || PyUnicode_Check(str))) {
PyErr_SetString(PyExc_TypeError, "not a valid transaction id"); PyErr_SetString(PyExc_TypeError, "not a valid transaction id");
@ -654,7 +588,7 @@ xid_recover(PyObject *conn)
PyObject *rv = NULL; PyObject *rv = NULL;
PyObject *curs = NULL; PyObject *curs = NULL;
PyObject *xids = NULL; PyObject *xids = NULL;
XidObject *xid = NULL; xidObject *xid = NULL;
PyObject *recs = NULL; PyObject *recs = NULL;
PyObject *rec = NULL; PyObject *rec = NULL;
PyObject *item = NULL; PyObject *item = NULL;
@ -693,34 +627,25 @@ xid_recover(PyObject *conn)
/* Get the xid with the XA triple set */ /* Get the xid with the XA triple set */
if (!(item = PySequence_GetItem(rec, 0))) { goto exit; } if (!(item = PySequence_GetItem(rec, 0))) { goto exit; }
if (!(xid = xid_from_string(item))) { goto exit; } if (!(xid = xid_from_string(item))) { goto exit; }
Py_DECREF(item); item = NULL; Py_CLEAR(item);
/* set xid.prepared */ /* set xid.prepared */
if (!(item = PySequence_GetItem(rec, 1))) { goto exit; } Py_CLEAR(xid->prepared);
tmp = xid->prepared; if (!(xid->prepared = PySequence_GetItem(rec, 1))) { goto exit; }
xid->prepared = item;
Py_DECREF(tmp);
item = NULL;
/* set xid.owner */ /* set xid.owner */
if (!(item = PySequence_GetItem(rec, 2))) { goto exit; } Py_CLEAR(xid->owner);
tmp = xid->owner; if (!(xid->owner = PySequence_GetItem(rec, 2))) { goto exit; }
xid->owner = item;
Py_DECREF(tmp);
item = NULL;
/* set xid.database */ /* set xid.database */
if (!(item = PySequence_GetItem(rec, 3))) { goto exit; } Py_CLEAR(xid->database);
tmp = xid->database; if (!(xid->database = PySequence_GetItem(rec, 3))) { goto exit; }
xid->database = item;
Py_DECREF(tmp);
item = NULL;
/* xid finished: add it to the returned list */ /* xid finished: add it to the returned list */
PyList_SET_ITEM(xids, i, (PyObject *)xid); PyList_SET_ITEM(xids, i, (PyObject *)xid);
xid = NULL; /* ref stolen */ xid = NULL; /* ref stolen */
Py_DECREF(rec); rec = NULL; Py_CLEAR(rec);
} }
/* set the return value. */ /* set the return value. */

Some files were not shown because too many files have changed in this diff Show More