summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--INSTALL8
-rw-r--r--ZPsycopgDA/DA.py208
-rw-r--r--ZPsycopgDA/DABase.py67
-rw-r--r--ZPsycopgDA/__init__.py10
-rw-r--r--ZPsycopgDA/db.py81
-rw-r--r--ZPsycopgDA/dtml/browse.dtml11
-rw-r--r--ZPsycopgDA/dtml/edit.dtml4
-rw-r--r--ZPsycopgDA/dtml/table_info.dtml7
-rw-r--r--psycopg/adapter_list.c4
-rw-r--r--psycopg/psycopgmodule.c5
-rw-r--r--setup.cfg2
12 files changed, 268 insertions, 150 deletions
diff --git a/ChangeLog b/ChangeLog
index 7314753..d8ccfb6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
* Release 2.0b2.
diff --git a/INSTALL b/INSTALL
index dd33fb5..512bd18 100644
--- a/INSTALL
+++ b/INSTALL
@@ -4,9 +4,8 @@ Compiling and installing psycopg
While psycopg 1.x used autoconf for its build process psycopg 2 switched to
the more pythoning setup.py. Currently both psycopg's author and distutils
have some limitations so the file setup.cfg is almost unused and most build
-options are hidden in setup.py. Before building psycopg look at the very
-first lines of setup.py and change any settings to follow your system (or
-taste); then:
+options are hidden in setup.py. Before building psycopg look at setup.cfg file
+and change any settings to follow your system (or taste); then:
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
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
diff --git a/ZPsycopgDA/DA.py b/ZPsycopgDA/DA.py
index 2ebb8e2..e61b3b1 100644
--- a/ZPsycopgDA/DA.py
+++ b/ZPsycopgDA/DA.py
@@ -18,25 +18,28 @@
# See the LICENSE file for details.
-ALLOWED_PSYCOPG_VERSIONS = ('2.0b2')
+ALLOWED_PSYCOPG_VERSIONS = ('2.0b2', '2.0b3')
import sys
+import time
import db
-import DABase
+
+import Acquisition
import Shared.DC.ZRDB.Connection
from db import DB
from Globals import DTMLFile
-from Globals import HTMLFile
from ImageFile import ImageFile
from ExtensionClass import Base
+from App.Dialogs import MessageDialog
from DateTime import DateTime
# import psycopg and functions/singletons needed for date/time conversions
import psycopg
-from psycopg import DATETIME
-from psycopg.extensions import TIME, DATE, INTERVAL
+from psycopg import NUMBER, STRING, ROWID, DATETIME
+from psycopg.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE
+from psycopg.extensions import TIME, INTERVAL
from psycopg.extensions import new_type, register_type
@@ -57,12 +60,14 @@ def manage_addZPsycopgConnection(self, id, title, connection_string,
# the connection object
-class Connection(DABase.Connection):
+class Connection(Shared.DC.ZRDB.Connection.Connection):
"""ZPsycopg Connection."""
- id = 'Psycopg_database_connection'
+ _isAnSQLConnection = 1
+
+ id = 'Psycopg_database_connection'
database_type = 'Psycopg'
meta_type = title = 'Z Psycopg Database Connection'
- icon = 'misc_/ZPsycopg/conn'
+ icon = 'misc_/ZPsycopgDA/conn'
def __init__(self, id, title, connection_string,
zdatetime, check=None, tilevel=2, encoding=''):
@@ -74,9 +79,8 @@ class Connection(DABase.Connection):
def factory(self):
return DB
- def table_info(self):
- return self._v_database_connection.table_info()
-
+ ## connection parameters editing ##
+
def edit(self, title, connection_string,
zdatetime, check=None, tilevel=2, encoding=''):
self.title = title
@@ -108,8 +112,8 @@ class Connection(DABase.Connection):
pass
# check psycopg version and raise exception if does not match
- if psycopg.__version__ not in ALLOWED_PSYCOPG_VERSIONS:
- raise ImportError("psycopg version mismatch (imported %s)" %s
+ if psycopg.__version__[:5] not in ALLOWED_PSYCOPG_VERSIONS:
+ raise ImportError("psycopg version mismatch (imported %s)" %
psycopg.__version__)
self.set_type_casts()
@@ -138,8 +142,48 @@ class Connection(DABase.Connection):
register_type(DATE)
register_type(TIME)
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,)
@@ -162,37 +206,38 @@ 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
+
+## zope-specific psycopg typecasters ##
# convert an ISO timestamp string from postgres to a Zope DateTime object
-def _cast_DateTime(str):
+def _cast_DateTime(str, curs):
if str:
# this will split us into [date, time, GMT/AM/PM(if there)]
- dt = split(str, ' ')
+ dt = str.split(' ')
if len(dt) > 1:
# we now should split out any timezone info
- dt[1] = split(dt[1], '-')[0]
- dt[1] = split(dt[1], '+')[0]
- return DateTime(join(dt[:2], ' '))
+ dt[1] = dt[1].split('-')[0]
+ dt[1] = dt[1].split('+')[0]
+ return DateTime(' '.join(dt[:2]))
else:
return DateTime(dt[0])
# convert an ISO date string from postgres to a Zope DateTime object
-def _cast_Date(str):
+def _cast_Date(str, curs):
if str:
return DateTime(str)
# Convert a time string from postgres to a Zope DateTime object.
# NOTE: we set the day as today before feeding to DateTime so
# that it has the same DST settings.
-def _cast_Time(str):
+def _cast_Time(str, curs):
if str:
return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
time.localtime(time.time())[:3]+
time.strptime(str[:8], "%H:%M:%S")[3:]))
# TODO: DateTime does not support intervals: what's the best we can do?
-def _cast_Interval(str):
+def _cast_Interval(str, curs):
return str
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)
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'
+ }
diff --git a/ZPsycopgDA/DABase.py b/ZPsycopgDA/DABase.py
deleted file mode 100644
index 03102c3..0000000
--- a/ZPsycopgDA/DABase.py
+++ /dev/null
@@ -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"""
diff --git a/ZPsycopgDA/__init__.py b/ZPsycopgDA/__init__.py
index b0e2a45..f9244ff 100644
--- a/ZPsycopgDA/__init__.py
+++ b/ZPsycopgDA/__init__.py
@@ -20,8 +20,6 @@
__doc__ = "ZPsycopg Database Adalper Registration."
__version__ = '2.0'
-import sys
-import string
import DA
methods = DA.folder_methods
@@ -30,3 +28,11 @@ meta_types = DA.meta_types
misc_ = DA.misc_
__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')
diff --git a/ZPsycopgDA/db.py b/ZPsycopgDA/db.py
index de2955d..738747d 100644
--- a/ZPsycopgDA/db.py
+++ b/ZPsycopgDA/db.py
@@ -22,12 +22,11 @@ from Shared.DC.ZRDB import dbi_db
from ZODB.POSException import ConflictError
-import time
import site
import pool
import psycopg
-from psycopg.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN
+from psycopg.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE
from psycopg import NUMBER, STRING, ROWID, DATETIME
@@ -91,6 +90,38 @@ class DB(TM, dbi_db.DB):
def sortKey(self):
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 ##
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)
except:
return ()
- res = []
- for name, type, width, ds, p, scale, null_ok in c.description:
- if type == NUMBER:
- if type == INTEGER:
- type = INTEGER
- elif type == FLOAT:
- type = FLOAT
- else: type = NUMBER
- elif type == BOOLEAN:
- type = BOOLEAN
- elif type == ROWID:
- type = ROWID
- elif type == DATETIME:
- type = DATETIME
- else:
- type = STRING
-
- res.append({'Name': name,
- 'Type': type.name,
- 'Precision': 0,
- 'Scale': 0,
- 'Nullable': 0})
self.putconn()
- return res
+ return self.convert_description(c.description, True)
## query execution ##
@@ -201,24 +210,4 @@ class DB(TM, dbi_db.DB):
self._abort()
raise err
- items = []
- for name, typ, width, ds, p, scale, null_ok in desc:
- if typ == NUMBER:
- if typ == INTEGER or typ == LONGINTEGER: typs = 'i'
- else: typs = 'n'
- elif typ == BOOLEAN:
- typs = 'n'
- elif typ == ROWID:
- typs = 'i'
- elif typ == DATETIME:
- typs = 'd'
- else:
- typs = 's'
- items.append({
- 'name': name,
- 'type': typs,
- 'width': width,
- 'null': null_ok,
- })
-
- return items, res
+ return self.convert_description(desc), res
diff --git a/ZPsycopgDA/dtml/browse.dtml b/ZPsycopgDA/dtml/browse.dtml
new file mode 100644
index 0000000..deffd0a
--- /dev/null
+++ b/ZPsycopgDA/dtml/browse.dtml
@@ -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>
diff --git a/ZPsycopgDA/dtml/edit.dtml b/ZPsycopgDA/dtml/edit.dtml
index 45275ed..7cb371f 100644
--- a/ZPsycopgDA/dtml/edit.dtml
+++ b/ZPsycopgDA/dtml/edit.dtml
@@ -45,10 +45,10 @@
<td align="left" valign="top">
<select name="tilevel:int">
<option value="1"
- <dtml-if expr="tilevel==1">selected="YES"</dtml-if">>
+ <dtml-if expr="tilevel==1">selected="YES"</dtml-if>>
Read committed</option>
<option value="2"
- <dtml-if expr="tilevel==2">selected="YES"</dtml-if">>
+ <dtml-if expr="tilevel==2">selected="YES"</dtml-if>>
Serializable</option>
</select>
</td>
diff --git a/ZPsycopgDA/dtml/table_info.dtml b/ZPsycopgDA/dtml/table_info.dtml
new file mode 100644
index 0000000..639c23f
--- /dev/null
+++ b/ZPsycopgDA/dtml/table_info.dtml
@@ -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>
diff --git a/psycopg/adapter_list.c b/psycopg/adapter_list.c
index 88db602..5d80356 100644
--- a/psycopg/adapter_list.c
+++ b/psycopg/adapter_list.c
@@ -22,10 +22,6 @@
#include <Python.h>
#include <structmember.h>
#include <stringobject.h>
-#include <datetime.h>
-
-#include <time.h>
-#include <string.h>
#define PSYCOPG_MODULE
#include "psycopg/config.h"
diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c
index 89d64a4..fd5c5d6 100644
--- a/psycopg/psycopgmodule.c
+++ b/psycopg/psycopgmodule.c
@@ -503,12 +503,15 @@ init_psycopg(void)
binaryType.tp_alloc = PyType_GenericAlloc;
isqlquoteType.tp_alloc = PyType_GenericAlloc;
pbooleanType.tp_alloc = PyType_GenericAlloc;
- pydatetimeType.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;
+#ifdef HAVE_PYDATETIME
+ pydatetimeType.tp_alloc = PyType_GenericAlloc;
+#endif
+
Dprintf("initpsycopg: module initialization complete");
}
diff --git a/setup.cfg b/setup.cfg
index 76b73d5..6d6f48e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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
# 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,
# but some extra checks on sys.platform will still be done in setup.py