ZPsycopgDA work.

This commit is contained in:
Federico Di Gregorio 2005-05-26 07:34:27 +00:00
parent a9ecbd0985
commit 1af8b57706
12 changed files with 268 additions and 150 deletions

View File

@ -1,3 +1,14 @@
2005-05-26 Federico Di Gregorio <fog@debian.org>
* ZPsycopgDA/db.py (DB.convert_description): isolated description
conversion (and fixed the conversion as per #18).
* ZPsycopgDA/DA.py: fixed again; this time Zope should work for
real. :/ Also fixed the type-casters (psycopg 2 added the extra
cursor parameter) as reported in #18.
* psycopg/psycopgmodule.c (init_psycopg): fixed Python 2.2 build.
2005-05-19 Federico Di Gregorio <fog@debian.org> 2005-05-19 Federico Di Gregorio <fog@debian.org>
* Release 2.0b2. * Release 2.0b2.

View File

@ -4,9 +4,8 @@ Compiling and installing psycopg
While psycopg 1.x used autoconf for its build process psycopg 2 switched to While psycopg 1.x used autoconf for its build process psycopg 2 switched to
the more pythoning setup.py. Currently both psycopg's author and distutils the more pythoning setup.py. Currently both psycopg's author and distutils
have some limitations so the file setup.cfg is almost unused and most build have some limitations so the file setup.cfg is almost unused and most build
options are hidden in setup.py. Before building psycopg look at the very options are hidden in setup.py. Before building psycopg look at setup.cfg file
first lines of setup.py and change any settings to follow your system (or and change any settings to follow your system (or taste); then:
taste); then:
python setup.py build python setup.py build
@ -42,7 +41,8 @@ Steps required to package your own version of psycopg:
1.1. create the file libpqdll.a (you should already know how to build 1.1. create the file libpqdll.a (you should already know how to build
Python extensions with mingw: see Python extensions with mingw: see
http://starship.python.net/crew/kernr/mingw32/Notes.html for details): http://starship.python.net/crew/kernr/mingw32/Notes.html for
details):
1.1.1. cd C:\Program Files\PostgreSQL\8.0\lib 1.1.1. cd C:\Program Files\PostgreSQL\8.0\lib

View File

@ -18,25 +18,28 @@
# See the LICENSE file for details. # See the LICENSE file for details.
ALLOWED_PSYCOPG_VERSIONS = ('2.0b2') ALLOWED_PSYCOPG_VERSIONS = ('2.0b2', '2.0b3')
import sys import sys
import time
import db import db
import DABase
import Acquisition
import Shared.DC.ZRDB.Connection import Shared.DC.ZRDB.Connection
from db import DB from db import DB
from Globals import DTMLFile from Globals import DTMLFile
from Globals import HTMLFile
from ImageFile import ImageFile from ImageFile import ImageFile
from ExtensionClass import Base from ExtensionClass import Base
from App.Dialogs import MessageDialog
from DateTime import DateTime from DateTime import DateTime
# import psycopg and functions/singletons needed for date/time conversions # import psycopg and functions/singletons needed for date/time conversions
import psycopg import psycopg
from psycopg import DATETIME from psycopg import NUMBER, STRING, ROWID, DATETIME
from psycopg.extensions import TIME, DATE, INTERVAL from psycopg.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE
from psycopg.extensions import TIME, INTERVAL
from psycopg.extensions import new_type, register_type from psycopg.extensions import new_type, register_type
@ -57,12 +60,14 @@ def manage_addZPsycopgConnection(self, id, title, connection_string,
# the connection object # the connection object
class Connection(DABase.Connection): class Connection(Shared.DC.ZRDB.Connection.Connection):
"""ZPsycopg Connection.""" """ZPsycopg Connection."""
id = 'Psycopg_database_connection' _isAnSQLConnection = 1
id = 'Psycopg_database_connection'
database_type = 'Psycopg' database_type = 'Psycopg'
meta_type = title = 'Z Psycopg Database Connection' meta_type = title = 'Z Psycopg Database Connection'
icon = 'misc_/ZPsycopg/conn' icon = 'misc_/ZPsycopgDA/conn'
def __init__(self, id, title, connection_string, def __init__(self, id, title, connection_string,
zdatetime, check=None, tilevel=2, encoding=''): zdatetime, check=None, tilevel=2, encoding=''):
@ -74,9 +79,8 @@ class Connection(DABase.Connection):
def factory(self): def factory(self):
return DB return DB
def table_info(self): ## connection parameters editing ##
return self._v_database_connection.table_info()
def edit(self, title, connection_string, def edit(self, title, connection_string,
zdatetime, check=None, tilevel=2, encoding=''): zdatetime, check=None, tilevel=2, encoding=''):
self.title = title self.title = title
@ -108,8 +112,8 @@ class Connection(DABase.Connection):
pass pass
# check psycopg version and raise exception if does not match # check psycopg version and raise exception if does not match
if psycopg.__version__ not in ALLOWED_PSYCOPG_VERSIONS: if psycopg.__version__[:5] not in ALLOWED_PSYCOPG_VERSIONS:
raise ImportError("psycopg version mismatch (imported %s)" %s raise ImportError("psycopg version mismatch (imported %s)" %
psycopg.__version__) psycopg.__version__)
self.set_type_casts() self.set_type_casts()
@ -138,8 +142,48 @@ class Connection(DABase.Connection):
register_type(DATE) register_type(DATE)
register_type(TIME) register_type(TIME)
register_type(INTERVAL) register_type(INTERVAL)
## browsing and table/column management ##
manage_options = Shared.DC.ZRDB.Connection.Connection.manage_options + (
{'label': 'Browse', 'action':'manage_browse'},)
manage_tables = DTMLFile('dtml/tables', globals())
manage_browse = DTMLFile('dtml/browse', globals())
info = None
# database connection registration data 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,) classes = (Connection,)
@ -162,37 +206,38 @@ for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin',
'int', 'float', 'date', 'time', 'datetime'): 'int', 'float', 'date', 'time', 'datetime'):
misc_[icon] = ImageFile('icons/%s.gif' % icon, globals()) misc_[icon] = ImageFile('icons/%s.gif' % icon, globals())
# zope-specific psycopg typecasters
## zope-specific psycopg typecasters ##
# convert an ISO timestamp string from postgres to a Zope DateTime object # convert an ISO timestamp string from postgres to a Zope DateTime object
def _cast_DateTime(str): def _cast_DateTime(str, curs):
if str: if str:
# this will split us into [date, time, GMT/AM/PM(if there)] # this will split us into [date, time, GMT/AM/PM(if there)]
dt = split(str, ' ') dt = str.split(' ')
if len(dt) > 1: if len(dt) > 1:
# we now should split out any timezone info # we now should split out any timezone info
dt[1] = split(dt[1], '-')[0] dt[1] = dt[1].split('-')[0]
dt[1] = split(dt[1], '+')[0] dt[1] = dt[1].split('+')[0]
return DateTime(join(dt[:2], ' ')) return DateTime(' '.join(dt[:2]))
else: else:
return DateTime(dt[0]) return DateTime(dt[0])
# convert an ISO date string from postgres to a Zope DateTime object # convert an ISO date string from postgres to a Zope DateTime object
def _cast_Date(str): def _cast_Date(str, curs):
if str: if str:
return DateTime(str) return DateTime(str)
# Convert a time string from postgres to a Zope DateTime object. # Convert a time string from postgres to a Zope DateTime object.
# NOTE: we set the day as today before feeding to DateTime so # NOTE: we set the day as today before feeding to DateTime so
# that it has the same DST settings. # that it has the same DST settings.
def _cast_Time(str): def _cast_Time(str, curs):
if str: if str:
return DateTime(time.strftime('%Y-%m-%d %H:%M:%S', return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
time.localtime(time.time())[:3]+ time.localtime(time.time())[:3]+
time.strptime(str[:8], "%H:%M:%S")[3:])) time.strptime(str[:8], "%H:%M:%S")[3:]))
# TODO: DateTime does not support intervals: what's the best we can do? # TODO: DateTime does not support intervals: what's the best we can do?
def _cast_Interval(str): def _cast_Interval(str, curs):
return str return str
ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime) ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime)
@ -200,3 +245,120 @@ ZINTERVAL = new_type((1186,), "ZINTERVAL", _cast_Interval)
ZDATE = new_type((1082,), "ZDATE", _cast_Date) ZDATE = new_type((1082,), "ZDATE", _cast_Date)
ZTIME = new_type((1083,), "ZTIME", _cast_Time) 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 = DTMLFile('table_info', globals())
menu = DTMLFile('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=DTMLFile('designInput',globals())
def manage_buildInput(self, id, source, default, REQUEST=None):
"Create a database method for an input form"
args=[]
values=[]
names=[]
columns=self._columns
for i in range(len(source)):
s=source[i]
if s=='Null': continue
c=columns[i]
d=default[i]
t=c['Type']
n=c['Name']
names.append(n)
if s=='Argument':
values.append("<dtml-sqlvar %s type=%s>'" %
(n, vartype(t)))
a='%s%s' % (n, boboType(t))
if d: a="%s=%s" % (a,d)
args.append(a)
elif s=='Property':
values.append("<dtml-sqlvar %s type=%s>'" %
(n, vartype(t)))
else:
if isStringType(t):
if find(d,"\'") >= 0: d=join(split(d,"\'"),"''")
values.append("'%s'" % d)
elif d:
values.append(str(d))
else:
raise ValueError, (
'no default was given for <em>%s</em>' % n)
class ColumnBrowser(Browser):
icon='field'
def check(self):
return ('\t<input type=checkbox name="%s.%s">' %
(self.TABLE_NAME, self._d['Name']))
def tpId(self): return self._d['Name']
def tpURL(self): return "Column/%s" % self._d['Name']
def Description(self):
d=self._d
if d['Scale']:
return " %(Type)s(%(Precision)s,%(Scale)s) %(Nullable)s" % d
else:
return " %(Type)s(%(Precision)s) %(Nullable)s" % d
table_icons={
'TABLE': 'table',
'VIEW':'view',
'SYSTEM_TABLE': 'stable',
}
field_icons={
NUMBER.name: 'i',
STRING.name: 'text',
DATETIME.name: 'date',
INTEGER.name: 'int',
FLOAT.name: 'float',
BOOLEAN.name: 'bin',
ROWID.name: 'int'
}

View File

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

View File

@ -20,8 +20,6 @@
__doc__ = "ZPsycopg Database Adalper Registration." __doc__ = "ZPsycopg Database Adalper Registration."
__version__ = '2.0' __version__ = '2.0'
import sys
import string
import DA import DA
methods = DA.folder_methods methods = DA.folder_methods
@ -30,3 +28,11 @@ meta_types = DA.meta_types
misc_ = DA.misc_ misc_ = DA.misc_
__ac_permissions__=DA.__ac_permissions__ __ac_permissions__=DA.__ac_permissions__
def initialize(context):
context.registerClass(
DA.Connection,
permission = 'Add Z Psycopg Database Connections',
constructors = (DA.manage_addZPsycopgConnectionForm,
DA.manage_addZPsycopgConnection),
icon = SOFTWARE_HOME + '/Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif')

View File

@ -22,12 +22,11 @@ from Shared.DC.ZRDB import dbi_db
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
import time
import site import site
import pool import pool
import psycopg import psycopg
from psycopg.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN from psycopg.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE
from psycopg import NUMBER, STRING, ROWID, DATETIME from psycopg import NUMBER, STRING, ROWID, DATETIME
@ -91,6 +90,38 @@ class DB(TM, dbi_db.DB):
def sortKey(self): def sortKey(self):
return 1 return 1
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:
if typ == NUMBER:
if typ == INTEGER or typ == LONGINTEGER:
typs = 'i'
else:
typs = 'n'
typp = NUMBER
elif typ == BOOLEAN:
typs = 'n'
typp = BOOLEAN
elif typ == ROWID:
typs = 'i'
typp = ROWID
elif typ == DATETIME or typ == DATE:
typs = 'd'
typp = DATETIME
else:
typs = 's'
typp = STRING
items.append({
'name': name,
'type': use_psycopg_types and typp or typs,
'width': width,
'precision': p,
'scale': scale,
'null': null_ok,
})
return items
## tables and rows ## ## tables and rows ##
def tables(self, rdb=0, _care=('TABLE', 'VIEW')): def tables(self, rdb=0, _care=('TABLE', 'VIEW')):
@ -119,30 +150,8 @@ class DB(TM, dbi_db.DB):
r = c.execute('SELECT * FROM "%s" WHERE 1=0' % table_name) r = c.execute('SELECT * FROM "%s" WHERE 1=0' % table_name)
except: except:
return () return ()
res = []
for name, type, width, ds, p, scale, null_ok in c.description:
if type == NUMBER:
if type == INTEGER:
type = INTEGER
elif type == FLOAT:
type = FLOAT
else: type = NUMBER
elif type == BOOLEAN:
type = BOOLEAN
elif type == ROWID:
type = ROWID
elif type == DATETIME:
type = DATETIME
else:
type = STRING
res.append({'Name': name,
'Type': type.name,
'Precision': 0,
'Scale': 0,
'Nullable': 0})
self.putconn() self.putconn()
return res return self.convert_description(c.description, True)
## query execution ## ## query execution ##
@ -201,24 +210,4 @@ class DB(TM, dbi_db.DB):
self._abort() self._abort()
raise err raise err
items = [] return self.convert_description(desc), res
for name, typ, width, ds, p, scale, null_ok in desc:
if typ == NUMBER:
if typ == INTEGER or typ == LONGINTEGER: typs = 'i'
else: typs = 'n'
elif typ == BOOLEAN:
typs = 'n'
elif typ == ROWID:
typs = 'i'
elif typ == DATETIME:
typs = 'd'
else:
typs = 's'
items.append({
'name': name,
'type': typs,
'width': width,
'null': null_ok,
})
return items, res

View File

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

View File

@ -45,10 +45,10 @@
<td align="left" valign="top"> <td align="left" valign="top">
<select name="tilevel:int"> <select name="tilevel:int">
<option value="1" <option value="1"
<dtml-if expr="tilevel==1">selected="YES"</dtml-if">> <dtml-if expr="tilevel==1">selected="YES"</dtml-if>>
Read committed</option> Read committed</option>
<option value="2" <option value="2"
<dtml-if expr="tilevel==2">selected="YES"</dtml-if">> <dtml-if expr="tilevel==2">selected="YES"</dtml-if>>
Serializable</option> Serializable</option>
</select> </select>
</td> </td>

View File

@ -0,0 +1,7 @@
<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>

View File

@ -22,10 +22,6 @@
#include <Python.h> #include <Python.h>
#include <structmember.h> #include <structmember.h>
#include <stringobject.h> #include <stringobject.h>
#include <datetime.h>
#include <time.h>
#include <string.h>
#define PSYCOPG_MODULE #define PSYCOPG_MODULE
#include "psycopg/config.h" #include "psycopg/config.h"

View File

@ -503,12 +503,15 @@ init_psycopg(void)
binaryType.tp_alloc = PyType_GenericAlloc; binaryType.tp_alloc = PyType_GenericAlloc;
isqlquoteType.tp_alloc = PyType_GenericAlloc; isqlquoteType.tp_alloc = PyType_GenericAlloc;
pbooleanType.tp_alloc = PyType_GenericAlloc; pbooleanType.tp_alloc = PyType_GenericAlloc;
pydatetimeType.tp_alloc = PyType_GenericAlloc;
connectionType.tp_alloc = PyType_GenericAlloc; connectionType.tp_alloc = PyType_GenericAlloc;
asisType.tp_alloc = PyType_GenericAlloc; asisType.tp_alloc = PyType_GenericAlloc;
qstringType.tp_alloc = PyType_GenericAlloc; qstringType.tp_alloc = PyType_GenericAlloc;
listType.tp_alloc = PyType_GenericAlloc; listType.tp_alloc = PyType_GenericAlloc;
chunkType.tp_alloc = PyType_GenericAlloc; chunkType.tp_alloc = PyType_GenericAlloc;
#ifdef HAVE_PYDATETIME
pydatetimeType.tp_alloc = PyType_GenericAlloc;
#endif
Dprintf("initpsycopg: module initialization complete"); Dprintf("initpsycopg: module initialization complete");
} }

View File

@ -5,7 +5,7 @@ define=PSYCOPG_EXTENSIONS,PSYCOPG_DISPLAY_SIZE,HAVE_PQFREEMEM,HAVE_PQPROTOCOL3
# PSYCOPG_OWN_QUOTING can be added above but it is deprecated # PSYCOPG_OWN_QUOTING can be added above but it is deprecated
# set to 1 to use Python datatime objects for date/time representation # set to 1 to use Python datatime objects for date/time representation
use_pydatetime=1 use_pydatetime=0
# include_dirs is the preferred method for locating postgresql headers, # include_dirs is the preferred method for locating postgresql headers,
# but some extra checks on sys.platform will still be done in setup.py # but some extra checks on sys.platform will still be done in setup.py