Merge branch 'devel'
1
.gitignore
vendored
|
@ -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
|
@ -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
|
360
ZPsycopgDA/DA.py
|
@ -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'
|
|
||||||
}
|
|
|
@ -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')
|
|
209
ZPsycopgDA/db.py
|
@ -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
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
Before Width: | Height: | Size: 897 B |
Before Width: | Height: | Size: 924 B |
Before Width: | Height: | Size: 930 B |
Before Width: | Height: | Size: 925 B |
Before Width: | Height: | Size: 915 B |
Before Width: | Height: | Size: 929 B |
Before Width: | Height: | Size: 918 B |
Before Width: | Height: | Size: 884 B |
Before Width: | Height: | Size: 878 B |
Before Width: | Height: | Size: 918 B |
Before Width: | Height: | Size: 926 B |
Before Width: | Height: | Size: 893 B |
Before Width: | Height: | Size: 894 B |
|
@ -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)
|
|
7
debian/README.zpsycopgda2
vendored
|
@ -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
|
@ -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
|
@ -1 +0,0 @@
|
||||||
5
|
|
64
debian/control
vendored
|
@ -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
|
@ -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
|
@ -1 +0,0 @@
|
||||||
2
|
|
3
debian/python-psycopg2da.dzproduct
vendored
|
@ -1,3 +0,0 @@
|
||||||
Name: psycopg2da
|
|
||||||
ZopeVersions: 3
|
|
||||||
Global: yes
|
|
93
debian/rules
vendored
|
@ -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
|
@ -1,2 +0,0 @@
|
||||||
version=3
|
|
||||||
http://www.initd.org/pub/software/psycopg/psycopg2-([0-9][0-9\.\-]*).tar.gz debian uupdate
|
|
4
debian/zope-psycopgda2.dzproduct
vendored
|
@ -1,4 +0,0 @@
|
||||||
Name: ZPsycopgDA
|
|
||||||
Directory: ZPsycopgDA:2
|
|
||||||
Package: zope-psycopgda2
|
|
||||||
ZopeVersions: >= 2.9
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,11 @@ The ``cursor`` class
|
||||||
`~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
|
||||||
|
|
||||||
Read-only boolean attribute: specifies if the cursor is closed
|
Read-only boolean attribute: specifies if the cursor is closed
|
||||||
|
@ -114,13 +119,44 @@ 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::
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
@ -294,6 +339,12 @@ The module exports the following constructors and singletons:
|
||||||
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
|
||||||
|
|
||||||
|
|
4
doc/src/news.rst
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Release notes
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. include:: ../../NEWS
|
39
doc/src/tools/lib/ticket_role.py
Normal 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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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())
|
|
||||||
|
|
149
lib/extras.py
|
@ -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
|
||||||
|
|
24
lib/pool.py
|
@ -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
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 *)¬ifyType,
|
||||||
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;
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) \
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,12 +1603,12 @@ 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;
|
||||||
|
@ -1586,6 +1622,54 @@ psyco_curs_withhold_set(cursorObject *self, PyObject *pyvalue)
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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*/
|
||||||
|
};
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -215,8 +214,7 @@ 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 */
|
||||||
|
@ -237,8 +235,7 @@ psyco_lobj_export(lobjectObject *self, PyObject *args)
|
||||||
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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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*/
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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) == ¬ifyType) {
|
||||||
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*/
|
||||||
¬ify_sequence, /*tp_as_sequence*/
|
¬ify_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*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conn->server_version >= 80300) {
|
||||||
|
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);
|
retvalue = pq_execute_command_locked(conn, "RESET ALL", pgres, error, tstate);
|
||||||
if (retvalue != 0) return retvalue;
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,36 +771,56 @@ 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(¬ifyType) = &PyType_Type;
|
||||||
|
if (PyType_Ready(¬ifyType) == -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;
|
||||||
|
@ -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*)¬ifyType);
|
||||||
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:
|
||||||
|
|
|
@ -31,32 +31,8 @@
|
||||||
#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
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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*/
|
||||||
|
|
||||||
/* Attribute descriptor and subclassing stuff */
|
|
||||||
|
|
||||||
0, /*tp_methods*/
|
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 will be set to PyType_GenericAlloc in module init*/
|
0, /*tp_alloc*/
|
||||||
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 *
|
||||||
|
|
|
@ -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] != '{') {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,22 @@
|
||||||
#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)
|
||||||
|
@ -45,9 +55,11 @@ psycopg_escape_string(PyObject *obj, const char *from, Py_ssize_t len,
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
#if PG_VERSION_HEX >= 0x080104
|
#if PG_VERSION_HEX >= 0x080104
|
||||||
|
@ -59,11 +71,15 @@ 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;
|
||||||
|
@ -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();
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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, >rid, &bqual))
|
&format_id, >rid, &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. */
|
||||||
|
|