+
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
{{pass}}
{{pass}}
From d238b5e86d41aa9a7418f4c67d331c2d377352ed Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Mon, 28 Jan 2013 20:48:02 -0600
Subject: [PATCH 08/39] Row.__delitem__
---
VERSION | 2 +-
gluon/dal.py | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/VERSION b/VERSION
index 98d8ae98..c9613f13 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.27.10.24.17
+Version 2.4.1-alpha.2+timestamp.2013.01.28.20.47.12
diff --git a/gluon/dal.py b/gluon/dal.py
index b48c0bb9..622c581c 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -6670,6 +6670,8 @@ class Row(object):
def __setitem__(self, key, value):
setattr(self, str(key), value)
+ __delitem__ = delattr
+
__copy__ = lambda self: Row(self)
__call__ = __getitem__
From 698286dbe09fa036bf19ea8b1679a14f2f996cc9 Mon Sep 17 00:00:00 2001
From: Massimo
Date: Tue, 29 Jan 2013 16:25:10 -0600
Subject: [PATCH 09/39] Query/Set enhancements for client/service apps, thanks
Alan
---
VERSION | 2 +-
gluon/dal.py | 147 +++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 144 insertions(+), 5 deletions(-)
diff --git a/VERSION b/VERSION
index c9613f13..66670966 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.28.20.47.12
+Version 2.4.1-alpha.2+timestamp.2013.01.29.16.24.13
diff --git a/gluon/dal.py b/gluon/dal.py
index 622c581c..e20b7cb3 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -7630,6 +7630,9 @@ def index():
query = self._adapter.id_query(query)
elif isinstance(query,Field):
query = query!=None
+ elif isinstance(query, dict):
+ icf = query.get("ignore_common_filters")
+ if icf: ignore_common_filters = icf
return Set(self, query, ignore_common_filters=ignore_common_filters)
def commit(self):
@@ -9135,8 +9138,8 @@ class Field(Expression):
def as_dict(self, flat=False, sanitize=True):
attrs = ("readable", "writable", "label", "default", "name",
"type", "represent", "compute")
- SERIALIZABLE = (int, long, basestring, dict, list, float,
- tuple, bool, None.__class__)
+ SERIALIZABLE_TYPES = (int, long, basestring, dict, list,
+ float, tuple, bool, type(None))
def flatten(obj):
if flat:
if isinstance(obj, flatten.__class__):
@@ -9146,7 +9149,7 @@ class Field(Expression):
obj = str(obj).split("'")[1]
except IndexError:
obj = str(obj)
- elif not isinstance(obj, SERIALIZABLE):
+ elif not isinstance(obj, SERIALIZABLE_TYPES):
obj = str(obj)
return obj
@@ -9161,7 +9164,7 @@ class Field(Expression):
else: other = v
r[k] = {flatten(type(v)):
filter_requires(type(v), other)}
- elif flat and (not isinstance(v, SERIALIZABLE)):
+ elif flat and (not isinstance(v, SERIALIZABLE_TYPES)):
r[k] = str(v)
return r
@@ -9253,7 +9256,61 @@ class Query(object):
def case(self,t=1,f=0):
return self.db._adapter.CASE(self,t,f)
+ def as_dict(self, flat=False, sanitize=True):
+ """Experimental stuff
+ This allows to return a plain dictionary with the basic
+ query representation. Can be used with json/xml services
+ for client-side db I/O
+
+ Example:
+ >>> q = db.auth_user.id != 0
+ >>> q.as_dict(flat=True)
+ {"op": "NE", "first":{"tablename": "auth_user",
+ "fieldname": "id"},
+ "second":0}
+ """
+ SERIALIZABLE_TYPES = (tuple, dict, list, int, long, float,
+ basestring, type(None), bool)
+ def loop(d):
+ newd = dict()
+ for k, v in d.items():
+ if k in ("first", "second"):
+ if isinstance(v, self.__class__):
+ newd[k] = loop(v.__dict__)
+ elif isinstance(v, Field):
+ newd[k] = {"tablename": v._tablename,
+ "fieldname": v.name}
+ elif isinstance(v, Expression):
+ newd[k] = loop(v.__dict__)
+ elif isinstance(v, SERIALIZABLE_TYPES):
+ newd[k] = v
+ else: pass
+ elif k == "op":
+ newd[k] = v.__name__
+ else: pass
+ return newd
+
+ if flat:
+ d = loop(self.__dict__)
+ else: d = self.__dict__
+ return d
+
+ def as_xml(self, sanitize=True):
+ if have_serializers:
+ xml = serializers.xml
+ else:
+ raise ImportError("No xml serializers available")
+ d = self.as_dict(flat=True, sanitize=sanitize)
+ return xml(d)
+
+ def as_json(self, sanitize=True):
+ if have_serializers:
+ json = serializers.json
+ else:
+ raise ImportError("No json serializers available")
+ d = self.as_dict(flat=True, sanitize=sanitize)
+ return json(d)
def xorify(orderby):
if not orderby:
@@ -9287,6 +9344,12 @@ class Set(object):
def __init__(self, db, query, ignore_common_filters = None):
self.db = db
self._db = db # for backward compatibility
+ self.dquery = None
+
+ # if query is a dict, parse it
+ if isinstance(query, dict):
+ query = self.parse(query)
+
if not ignore_common_filters is None and \
use_common_filters(query) == ignore_common_filters:
query = copy.copy(query)
@@ -9334,6 +9397,82 @@ class Set(object):
fields = db[tablename]._listify(update_fields,update=True)
return db._adapter._update(tablename,self.query,fields)
+ def as_dict(self, flat=False, sanitize=True):
+ if flat:
+ uid = dbname = uri = None
+ codec = self.db._db_codec
+ if not sanitize:
+ uri, dbname, uid = (self.db._dbname, str(self.db),
+ self.db._db_uid)
+ d = {"query": self.query.as_dict(flat=flat)}
+ d["db"] = {"uid": uid, "codec": codec,
+ "name": dbname, "uri": uri}
+ return d
+ else: return self.__dict__
+
+ def as_xml(self, sanitize=True):
+ if have_serializers:
+ xml = serializers.xml
+ else:
+ raise ImportError("No xml serializers available")
+ d = self.as_dict(flat=True, sanitize=sanitize)
+ return xml(d)
+
+ def as_json(self, sanitize=True):
+ if have_serializers:
+ json = serializers.json
+ else:
+ raise ImportError("No json serializers available")
+ d = self.as_dict(flat=True, sanitize=sanitize)
+ return json(d)
+
+ def parse(self, dquery):
+ "Experimental: Turn a dictionary into a Query object"
+ self.dquery = dquery
+ return self.build(self.dquery)
+
+ def build(self, d):
+ "Experimental: see .parse()"
+ op, first, second = (d["op"], d["first"],
+ d.get("second", None))
+ left = right = built = None
+
+ if op in ("AND", "OR"):
+ if not (type(first), type(second)) == (dict, dict):
+ raise SyntaxError("Invalid AND/OR query")
+ if op == "AND":
+ built = self.build(first) & self.build(second)
+ else: built = self.build(first) | self.build(second)
+
+ elif op == "NOT":
+ if first is None:
+ raise SyntaxError("Invalid NOT query")
+ built = ~self.build(first)
+ else:
+ # normal operation (GT, EQ, LT, ...)
+ for k, v in {"left": first, "right": second}.items():
+ if isinstance(v, dict) and v.get("op"):
+ v = self.build(v)
+ if isinstance(v, dict) and ("tablename" in v):
+ v = self.db[v["tablename"]][v["fieldname"]]
+ if k == "left":
+ left = v
+ else:
+ right = v
+
+ if op == "EQ": built = left == right
+ elif op == "NE": built = left != right
+ elif op == "GT": built = left > right
+ elif op == "GE": built = left >= right
+ elif op == "LT": built = left < right
+ elif op == "LE": built = left <= right
+ elif op == "CONTAINS": built = left.contains(right)
+ elif op == "BELONGS": built = left.belongs(right)
+ elif op == "INVERT": built = ~left
+ else: raise SyntaxError("Operator not supported")
+
+ return built
+
def isempty(self):
return not self.select(limitby=(0,1))
From 45969c7628e2f618fda3effd4ca95ce75facf61e Mon Sep 17 00:00:00 2001
From: Massimo
Date: Tue, 29 Jan 2013 16:27:46 -0600
Subject: [PATCH 10/39] added note to
scripts/setup-web2py-nginx-uwsgi-on-centos.sh, thanks Alan
---
VERSION | 2 +-
scripts/setup-web2py-nginx-uwsgi-on-centos.sh | 52 ++++++++++++++-----
2 files changed, 39 insertions(+), 15 deletions(-)
diff --git a/VERSION b/VERSION
index 66670966..35c6106a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.29.16.24.13
+Version 2.4.1-alpha.2+timestamp.2013.01.29.16.27.18
diff --git a/scripts/setup-web2py-nginx-uwsgi-on-centos.sh b/scripts/setup-web2py-nginx-uwsgi-on-centos.sh
index 0892026d..09f1b81b 100644
--- a/scripts/setup-web2py-nginx-uwsgi-on-centos.sh
+++ b/scripts/setup-web2py-nginx-uwsgi-on-centos.sh
@@ -1,19 +1,43 @@
#!/bin/bash
-# Script for installing Web2py with Nginx and Uwsgi on Centos 5
-# Created By Hutchinson
-# Modified by spametki
-# License: BSD
-
-# It was originally posted in this web2py-users group thread:
-# https://groups.google.com/forum/?fromgroups#!topic/web2py/O4c4Jfr18tM
-
-# There are lots of subtleties of ownership, and one has to take care
-# when installing python 2.6 not to stop the systems python2.4 from working.
-
-# NOTE: The only thing that should need changing for
-# each installation is the $BASEARCH (base architecture) of the machine.
-# This is determined by doing uname -i. This is needed for the nginx installation.
+# -------------------------------------------------------------------
+# Description : Installation and basic configuration of web2py,
+# uWSGI, andNGINX.
+# in CentOS 5.x GNU/Linux
+# Usage : Copy the script in /home/username and run it as root,
+# you may need to allow execution (chmod +x)
+#
+# WARNING: This script was modified to install compiled
+# versions of Python and other packages that may be
+# available at your Centos package repository.
+# This change was made in order to get the latest
+# stable libraries available for avoiding compatibility
+# issues.
+#
+# It was originally posted in this web2py-users group
+# thread: https://groups.google.com/forum/?fromgroups#
+# !topic/web2py/O4c4Jfr18tM
+#
+# There are lots of subtleties of ownership, and one
+# has to take care when installing python 2.6 not to
+# stop the systems python2.4 from working.
+#
+# NOTE: The only thing that should need changing for
+# each installation is the $BASEARCH (base
+# architecture)
+# of the machine. This is determined by doing uname -i.
+# This is needed for the nginx installation.
+#
+# File : setup-web2py-nginx-uwsgi-on-centos.sh
+# Author : Hutchinson
+# Modified by : Alan Etkin
+# Email : spametki@gmail.com
+# Copyright : web2py
+# Date : 2013-01-28
+# Disclaimers : This script is provided "as is", without warranty of
+# any kind.
+# Licence : BSD
+# -------------------------------------------------------------------
# Retrieve base architecture
BASEARCH=$(uname -i)
From 8740fea7c1c10fb0c2648348eda5dbeab2e63236 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 30 Jan 2013 09:29:49 -0600
Subject: [PATCH 11/39] allow multiple left joins in grid
---
VERSION | 2 +-
gluon/sqlhtml.py | 5 ++++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index 35c6106a..db093562 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.29.16.27.18
+Version 2.4.1-alpha.2+timestamp.2013.01.30.09.29.07
diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py
index bf9e8ea0..90ccf722 100644
--- a/gluon/sqlhtml.py
+++ b/gluon/sqlhtml.py
@@ -1867,7 +1867,10 @@ class SQLFORM(FORM):
dbset = db(query)
tablenames = db._adapter.tables(dbset.query)
if left is not None:
- tablenames += db._adapter.tables(left)
+ if not isinstance(left, (list, tuple)):
+ left = [left]
+ for join in left:
+ tablenames += db._adapter.tables(join)
tables = [db[tablename] for tablename in tablenames]
if not fields:
fields = reduce(lambda a, b: a + b,
From 14bd35e348154fd66d8ed63b4d44e248045d52f9 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 30 Jan 2013 09:41:41 -0600
Subject: [PATCH 12/39] fixed issue 1306 rendering of width in markmin, thanks
dickschrauwen
---
VERSION | 2 +-
gluon/contrib/markmin/markmin2html.py | 27 ++++++++++++++-------------
2 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/VERSION b/VERSION
index db093562..863d2fec 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.30.09.29.07
+Version 2.4.1-alpha.2+timestamp.2013.01.30.09.40.42
diff --git a/gluon/contrib/markmin/markmin2html.py b/gluon/contrib/markmin/markmin2html.py
index 0f56b603..cbdcec47 100755
--- a/gluon/contrib/markmin/markmin2html.py
+++ b/gluon/contrib/markmin/markmin2html.py
@@ -609,7 +609,7 @@ def protolinks_simple(proto, url):
#elif proto == 'embed': # NOTE: embed is a synonym to iframe now
# return '%s>'%(url,class_prefix,url)
elif proto == 'qr':
- return ''%url
+ return ''%url
return proto+':'+url
def render(text,
@@ -701,7 +701,7 @@ def render(text,
'
'
>>> render('[[this is an image http://example.com left 200px]]')
- '
'
+ '
'
>>> render("[[Your browser doesn't support
'
+ '
title11:
'
>>> render(r"\\[[probe]]")
'
[[probe]]
'
@@ -1236,23 +1236,24 @@ def render(text,
return m.group(0)
k = escape(k)
t = t or ''
- width = ' width="%s"' % w if w else ''
+ style = 'width:%s' % w if w else ''
title = ' title="%s"' % escape(a).replace(META, DISABLED_META) if a else ''
- style = p_begin = p_end = ''
+ p_begin = p_end = ''
if p == 'center':
p_begin = '
'
p_end = '
'+pp
elif p in ('left','right'):
- style = ' style="float:%s"' % p
+ style = ('float:%s' % p)+(';%s' % style if style else '')
+ if style:
+ style = ' style="%s"' % style
if p in ('video','audio'):
t = render(t, {}, {}, 'br', URL, environment, latex,
autolinks, protolinks, class_prefix, id_prefix, pretty_print)
- return '<%(p)s controls="controls"%(title)s%(width)s>%(t)s%(p)s>' \
- % dict(p=p, title=title, width=width, k=k, t=t)
+ return '<%(p)s controls="controls"%(title)s%(style)s>%(t)s%(p)s>' \
+ % dict(p=p, title=title, style=style, k=k, t=t)
alt = ' alt="%s"'%escape(t).replace(META, DISABLED_META) if t else ''
- return '%(begin)s%(end)s' \
- % dict(begin=p_begin, k=k, alt=alt, title=title,
- style=style, width=width, end=p_end)
+ return '%(begin)s%(end)s' \
+ % dict(begin=p_begin, k=k, alt=alt, title=title, style=style, end=p_end)
def sub_link(m):
t,a,k,p = m.group('t','a','k','p')
From 0719af011b916d268b58b015195ebf429b461cd7 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 30 Jan 2013 09:45:00 -0600
Subject: [PATCH 13/39] operator serilization in DAL, thanks Alan
---
VERSION | 2 +-
gluon/dal.py | 57 +++++++++++++++++++++++++++++++++++++---------------
2 files changed, 42 insertions(+), 17 deletions(-)
diff --git a/VERSION b/VERSION
index 863d2fec..407e7a65 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.30.09.40.42
+Version 2.4.1-alpha.2+timestamp.2013.01.30.09.44.09
diff --git a/gluon/dal.py b/gluon/dal.py
index e20b7cb3..85b70b9d 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -9253,6 +9253,12 @@ class Query(object):
return self.first
return Query(self.db,self.db._adapter.NOT,self)
+ def __eq__(self, other):
+ return repr(self) == repr(other)
+
+ def __ne__(self, other):
+ return not (self == other)
+
def case(self,t=1,f=0):
return self.db._adapter.CASE(self,t,f)
@@ -9270,6 +9276,7 @@ class Query(object):
"fieldname": "id"},
"second":0}
"""
+
SERIALIZABLE_TYPES = (tuple, dict, list, int, long, float,
basestring, type(None), bool)
def loop(d):
@@ -9285,16 +9292,22 @@ class Query(object):
newd[k] = loop(v.__dict__)
elif isinstance(v, SERIALIZABLE_TYPES):
newd[k] = v
- else: pass
elif k == "op":
- newd[k] = v.__name__
- else: pass
+ if callable(v):
+ newd[k] = v.__name__
+ elif isinstance(v, basestring):
+ newd[k] = v
+ else: pass # not callable or string
+ elif isinstance(v, SERIALIZABLE_TYPES):
+ if isinstance(v, dict):
+ newd[k] = loop(v)
+ else: newd[k] = v
return newd
if flat:
- d = loop(self.__dict__)
- else: d = self.__dict__
- return d
+ return loop(self.__dict__)
+ else: return self.__dict__
+
def as_xml(self, sanitize=True):
if have_serializers:
@@ -9368,10 +9381,10 @@ class Set(object):
query = query!=None
if self.query:
return Set(self.db, self.query & query,
- ignore_common_filters = ignore_common_filters)
+ ignore_common_filters=ignore_common_filters)
else:
return Set(self.db, query,
- ignore_common_filters = ignore_common_filters)
+ ignore_common_filters=ignore_common_filters)
def _count(self,distinct=None):
return self.db._adapter._count(self.query,distinct)
@@ -9455,10 +9468,11 @@ class Set(object):
v = self.build(v)
if isinstance(v, dict) and ("tablename" in v):
v = self.db[v["tablename"]][v["fieldname"]]
- if k == "left":
- left = v
- else:
- right = v
+ if k == "left": left = v
+ else: right = v
+
+ if hasattr(self.db._adapter, op):
+ opm = getattr(self.db._adapter, op)
if op == "EQ": built = left == right
elif op == "NE": built = left != right
@@ -9466,10 +9480,21 @@ class Set(object):
elif op == "GE": built = left >= right
elif op == "LT": built = left < right
elif op == "LE": built = left <= right
- elif op == "CONTAINS": built = left.contains(right)
- elif op == "BELONGS": built = left.belongs(right)
- elif op == "INVERT": built = ~left
- else: raise SyntaxError("Operator not supported")
+ elif op in ("JOIN", "LEFT_JOIN", "RANDOM", "ALLOW_NULL"):
+ built = Expression(self.db, opm)
+ elif op in ("LOWER", "UPPER", "EPOCH", "PRIMARY_KEY",
+ "COALESCE_ZERO", "RAW", "INVERT"):
+ built = Expression(self.db, opm, left)
+ elif op in ("COUNT", "EXTRACT", "AGGREGATE", "SUBSTRING",
+ "REGEXP", "LIKE", "ILIKE", "STARTSWITH",
+ "ENDSWITH", "ADD", "SUB", "MUL", "DIV",
+ "MOD", "AS", "ON", "COMMA", "NOT_NULL",
+ "COALESCE", "CONTAINS", "BELONGS"):
+ built = Expression(self.db, opm, left, right)
+ # expression as string
+ elif not (left or right): built = Expression(self.db, op)
+ else:
+ raise SyntaxError("Operator not supported: %s" % op)
return built
From 0328ed49b9814a36e6b7b8be0b49236ae354d1cf Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 30 Jan 2013 12:30:23 -0600
Subject: [PATCH 14/39] fixed mssql3 groupby/orderby issue, thanks Niphlod.
---
VERSION | 2 +-
applications/admin/languages/it.py | 11 +++++++++++
gluon/dal.py | 17 ++++++++++++++++-
3 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index 407e7a65..dafb2b27 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.30.09.44.09
+Version 2.4.1-alpha.2+timestamp.2013.01.30.12.29.40
diff --git a/applications/admin/languages/it.py b/applications/admin/languages/it.py
index 957fe0bf..98bc43b6 100644
--- a/applications/admin/languages/it.py
+++ b/applications/admin/languages/it.py
@@ -23,6 +23,7 @@
'Admin language': 'Admin language',
'administrative interface': 'administrative interface',
'Administrator Password:': 'Password Amministratore:',
+'An error occured, please %s the page': 'An error occured, please %s the page',
'and rename it (required):': 'e rinominala (obbligatorio):',
'and rename it:': 'e rinominala:',
'appadmin': 'appadmin ',
@@ -144,8 +145,10 @@
'Get from URL:': 'Get from URL:',
'Git Pull': 'Git Pull',
'Git Push': 'Git Push',
+'graph model': 'graph model',
'Hello World': 'Salve Mondo',
'Help': 'aiuto',
+'Hide/Show Translated strings': 'Hide/Show Translated strings',
'htmledit': 'modifica come html',
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
'Import/Export': 'Importa/Esporta',
@@ -195,6 +198,8 @@
'NO': 'NO',
'No databases in this application': 'Nessun database presente in questa applicazione',
'no match': 'nessuna corrispondenza',
+'or alternatively': 'or alternatively',
+'Or Get from URL:': 'Or Get from URL:',
'or import from csv file': 'oppure importa da file CSV',
'or provide app url:': "oppure fornisci url dell'applicazione:",
'Original/Translation': 'Originale/Traduzione',
@@ -219,6 +224,7 @@
'record does not exist': 'il record non esiste',
'record id': 'ID del record',
'register': 'registrazione',
+'reload': 'reload',
'Remove compiled': 'rimozione codice compilato',
'request': 'request',
'Resolve Conflict file': 'File di risoluzione conflitto',
@@ -242,6 +248,7 @@
'Start wizard': 'start wizard',
'state': 'stato',
'static': 'statico',
+'Static': 'Static',
'Static files': 'Files statici',
'Stylesheet': 'Foglio di stile (stylesheet)',
'submit': 'invia',
@@ -261,6 +268,7 @@
'There are no models': 'Non ci sono modelli',
'There are no modules': 'Non ci sono moduli',
'There are no plugins': 'There are no plugins',
+'There are no private files': 'There are no private files',
'There are no static files': 'Non ci sono file statici',
'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base',
'There are no views': 'Non ci sono viste ("view")',
@@ -280,6 +288,7 @@
'Translation strings for the application': 'Translation strings for the application',
'try': 'prova',
'try something like': 'prova qualcosa come',
+'Try the mobile interface': 'Try the mobile interface',
'try view': 'try view',
'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti',
'unable to create application "%s"': 'impossibile creare applicazione "%s"',
@@ -298,6 +307,7 @@
'Update:': 'Aggiorna:',
'upgrade web2py now': 'upgrade web2py now',
'upload': 'upload',
+'Upload': 'Upload',
'Upload & install packed application': 'Carica ed installa pacchetto con applicazione',
'Upload a package:': 'Upload a package:',
'Upload and install packed application': 'Upload and install packed application',
@@ -308,6 +318,7 @@
'Use an url:': 'Use an url:',
'variables': 'variables',
'Version': 'Versione',
+'Version %s.%s.%s %s (%s)': 'Version %s.%s.%s %s (%s)',
'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s',
'versioning': 'sistema di versioni',
'Versioning': 'Versioning',
diff --git a/gluon/dal.py b/gluon/dal.py
index 85b70b9d..4e3945a2 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -3063,6 +3063,8 @@ class MSSQLAdapter(BaseAdapter):
if limitby:
(lmin, lmax) = limitby
sql_s += ' TOP %i' % lmax
+ if 'GROUP BY' in sql_o:
+ sql_o = sql_o[:sql_o.find('ORDER BY ')]
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
TRUE = 1
@@ -3205,7 +3207,18 @@ class MSSQL3Adapter(MSSQLAdapter):
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
if limitby:
(lmin, lmax) = limitby
- return 'SELECT %s FROM (SELECT %s ROW_NUMBER() over (order by id) AS w_row, %s FROM %s%s%s) TMP WHERE w_row BETWEEN %i AND %s;' % (sql_f,sql_s,sql_f,sql_t,sql_w,sql_o,lmin,lmax)
+ if lmin == 0:
+ sql_s += ' TOP %i' % lmax
+ return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
+ lmin += 1
+ sql_o_inner = sql_o[sql_o.find('ORDER BY ')+9:]
+ sql_g_inner = sql_o[:sql_o.find('ORDER BY ')]
+ sql_f_outer = ['f_%s' % f for f in range(len(sql_f.split(',')))]
+ sql_f_inner = [f for f in sql_f.split(',')]
+ sql_f_iproxy = ['%s AS %s' % (o, n) for (o, n) in zip(sql_f_inner, sql_f_outer)]
+ sql_f_iproxy = ', '.join(sql_f_iproxy)
+ sql_f_oproxy = ', '.join(sql_f_outer)
+ return 'SELECT %s %s FROM (SELECT %s ROW_NUMBER() OVER (ORDER BY %s) AS w_row, %s FROM %s%s%s) TMP WHERE w_row BETWEEN %i AND %s;' % (sql_s,sql_f_oproxy,sql_s,sql_f,sql_f_iproxy,sql_t,sql_w,sql_g_inner,lmin,lmax)
return 'SELECT %s %s FROM %s%s%s;' % (sql_s,sql_f,sql_t,sql_w,sql_o)
def rowslice(self,rows,minimum=0,maximum=None):
return rows
@@ -3249,6 +3262,7 @@ class MSSQL2Adapter(MSSQLAdapter):
def execute(self,a):
return self.log_execute(a.decode('utf8'))
+
class SybaseAdapter(MSSQLAdapter):
drivers = ('Sybase',)
@@ -6508,6 +6522,7 @@ ADAPTERS = {
'oracle': OracleAdapter,
'mssql': MSSQLAdapter,
'mssql2': MSSQL2Adapter,
+ 'mssql3': MSSQL3Adapter,
'sybase': SybaseAdapter,
'db2': DB2Adapter,
'teradata': TeradataAdapter,
From ef06262393e64091c962fade688f92be6c18d034 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 30 Jan 2013 12:48:08 -0600
Subject: [PATCH 15/39] returning more info from auth.wiki, thanks Paolo
---
VERSION | 2 +-
gluon/tools.py | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index dafb2b27..e361f266 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.30.12.29.40
+Version 2.4.1-alpha.2+timestamp.2013.01.30.12.47.24
diff --git a/gluon/tools.py b/gluon/tools.py
index 6b2da2c4..eb3489b2 100644
--- a/gluon/tools.py
+++ b/gluon/tools.py
@@ -5092,7 +5092,12 @@ class Wiki(object):
url = URL(args=('_edit', slug))
return dict(content=A('Create page "%s"' % slug, _href=url, _class="btn"))
else:
- return dict(content=XML(self.fix_hostname(page.html)))
+ return dict(title=page.title,
+ slug=page.slug,
+ content=XML(self.fix_hostname(page.html)),
+ tags=page.tags,
+ created_on=page.created_on,
+ modified_on=page.modified_on)
elif current.request.extension == 'load':
return self.fix_hostname(page.html) if page else ''
else:
From d407bdbbbcb23643e0cd1c9d565f47d2550bd988 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 30 Jan 2013 14:25:49 -0600
Subject: [PATCH 16/39] better scripts/setup-web2py-nginx-uwsgi-on-centos.sh,
thanks Alan
---
VERSION | 2 +-
scripts/setup-web2py-nginx-uwsgi-on-centos.sh | 20 +++++++++++++------
2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/VERSION b/VERSION
index e361f266..52249e85 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.30.12.47.24
+Version 2.4.1-alpha.2+timestamp.2013.01.30.14.25.06
diff --git a/scripts/setup-web2py-nginx-uwsgi-on-centos.sh b/scripts/setup-web2py-nginx-uwsgi-on-centos.sh
index 09f1b81b..c10e3951 100644
--- a/scripts/setup-web2py-nginx-uwsgi-on-centos.sh
+++ b/scripts/setup-web2py-nginx-uwsgi-on-centos.sh
@@ -12,7 +12,13 @@
# available at your Centos package repository.
# This change was made in order to get the latest
# stable libraries available for avoiding compatibility
-# issues.
+#
+# A bug was reported for the 2.7.3 version of python
+# here http://bugs.python.org/issue14572
+# in case you choose to change to VERSION=2.7
+# (see below) mind that the automatic patch applied
+# could not work for your environment. By default,
+# Python 2.6 is installed.
#
# It was originally posted in this web2py-users group
# thread: https://groups.google.com/forum/?fromgroups#
@@ -29,7 +35,7 @@
# This is needed for the nginx installation.
#
# File : setup-web2py-nginx-uwsgi-on-centos.sh
-# Author : Hutchinson
+# Author : Peter Hutchinson
# Modified by : Alan Etkin
# Email : spametki@gmail.com
# Copyright : web2py
@@ -56,8 +62,8 @@ bzip2-devel sqlite-devel db4-devel openssl-devel tk-devel bluez-libs-devel
# to fit your deployment needs.
# Python options
-PREFIX=2.7
-VERSION=2.7.3
+PREFIX=2.6
+VERSION=2.6.8
# uWSGI options
version=uwsgi-1.2.4
@@ -71,8 +77,10 @@ wget http://www.python.org/ftp/python/$VERSION/Python-$VERSION.tgz
tar xvfz Python-$VERSION.tgz
cd Python-$VERSION
-echo "Applying patch for sqlite3 bug from post http://bugs.python.org/msg161076"
-curl -sk https://raw.github.com/gist/2727063/ | patch -p1
+if [ "$VERSION" == "2.7.3" ]; then
+ echo "Applying patch for sqlite3 bug from post http://bugs.python.org/msg161076"
+ curl -sk https://raw.github.com/gist/2727063/ | patch -p1
+fi
./configure --prefix=/opt/python$PREFIX --with-threads --enable-shared
make
From ecdd7f6733904cb8b235300394b6163f6fe752b0 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Fri, 1 Feb 2013 21:16:21 -0600
Subject: [PATCH 17/39] fixed menus in auth.wiki()
---
VERSION | 2 +-
applications/welcome/models/menu.py | 1 +
gluon/tools.py | 11 +++++++----
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/VERSION b/VERSION
index 52249e85..b0e72bcc 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.01.30.14.25.06
+Version 2.4.1-alpha.2+timestamp.2013.02.01.21.15.32
diff --git a/applications/welcome/models/menu.py b/applications/welcome/models/menu.py
index 87d300a8..c303fbe9 100644
--- a/applications/welcome/models/menu.py
+++ b/applications/welcome/models/menu.py
@@ -10,6 +10,7 @@ response.logo = A(B('web',SPAN(2),'py'),XML('™ '),
response.title = ' '.join(
word.capitalize() for word in request.application.split('_'))
response.subtitle = T('customize me!')
+response.masterhad = None
## read more at http://dev.w3.org/html5/markup/meta.name.html
response.meta.author = 'Your Name '
diff --git a/gluon/tools.py b/gluon/tools.py
index eb3489b2..ff723bdf 100644
--- a/gluon/tools.py
+++ b/gluon/tools.py
@@ -4904,7 +4904,8 @@ class Wiki(object):
self.force_prefix = force_prefix % self.auth.user
else:
self.force_prefix = force_prefix
- self.host = current.request.env.http_host
+ request = current.request
+ self.host = request.env.http_host
perms = self.manage_permissions = manage_permissions
self.restrict_search = restrict_search
self.extra = extra or {}
@@ -4980,12 +4981,16 @@ class Wiki(object):
db.wiki_tag.insert(name=tag, wiki_page=page.id)
db.wiki_page._after_insert.append(update_tags_insert)
db.wiki_page._after_update.append(update_tags_update)
- if auth.user and check_credentials(current.request) and \
+ if auth.user and check_credentials(request) and \
not 'wiki_editor' in auth.user_groups.values():
group = db.auth_group(role='wiki_editor')
gid = group.id if group else db.auth_group.insert(
role='wiki_editor')
auth.add_membership(gid)
+
+ automenu = self.menu(request.controller, request.function)
+ current.response.menu += automenu
+
# WIKI ACCESS POLICY
def not_authorized(self, page=None):
@@ -5034,8 +5039,6 @@ class Wiki(object):
def __call__(self):
request = current.request
- automenu = self.menu(request.controller, request.function)
- current.response.menu += automenu
zero = request.args(0) or 'index'
if zero and zero.isdigit():
return self.media(int(zero))
From a6d4e2340e052158f262d9b834cac616d3e2b540 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Fri, 1 Feb 2013 21:24:30 -0600
Subject: [PATCH 18/39] reverted last change, better to use wiki.menu
---
VERSION | 2 +-
applications/welcome/models/menu.py | 1 -
gluon/tools.py | 11 ++++-------
3 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/VERSION b/VERSION
index b0e72bcc..e3791167 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.01.21.15.32
+Version 2.4.1-alpha.2+timestamp.2013.02.01.21.23.40
diff --git a/applications/welcome/models/menu.py b/applications/welcome/models/menu.py
index c303fbe9..87d300a8 100644
--- a/applications/welcome/models/menu.py
+++ b/applications/welcome/models/menu.py
@@ -10,7 +10,6 @@ response.logo = A(B('web',SPAN(2),'py'),XML('™ '),
response.title = ' '.join(
word.capitalize() for word in request.application.split('_'))
response.subtitle = T('customize me!')
-response.masterhad = None
## read more at http://dev.w3.org/html5/markup/meta.name.html
response.meta.author = 'Your Name '
diff --git a/gluon/tools.py b/gluon/tools.py
index ff723bdf..eb3489b2 100644
--- a/gluon/tools.py
+++ b/gluon/tools.py
@@ -4904,8 +4904,7 @@ class Wiki(object):
self.force_prefix = force_prefix % self.auth.user
else:
self.force_prefix = force_prefix
- request = current.request
- self.host = request.env.http_host
+ self.host = current.request.env.http_host
perms = self.manage_permissions = manage_permissions
self.restrict_search = restrict_search
self.extra = extra or {}
@@ -4981,16 +4980,12 @@ class Wiki(object):
db.wiki_tag.insert(name=tag, wiki_page=page.id)
db.wiki_page._after_insert.append(update_tags_insert)
db.wiki_page._after_update.append(update_tags_update)
- if auth.user and check_credentials(request) and \
+ if auth.user and check_credentials(current.request) and \
not 'wiki_editor' in auth.user_groups.values():
group = db.auth_group(role='wiki_editor')
gid = group.id if group else db.auth_group.insert(
role='wiki_editor')
auth.add_membership(gid)
-
- automenu = self.menu(request.controller, request.function)
- current.response.menu += automenu
-
# WIKI ACCESS POLICY
def not_authorized(self, page=None):
@@ -5039,6 +5034,8 @@ class Wiki(object):
def __call__(self):
request = current.request
+ automenu = self.menu(request.controller, request.function)
+ current.response.menu += automenu
zero = request.args(0) or 'index'
if zero and zero.isdigit():
return self.media(int(zero))
From af7a922caca66c38b5c08d23602faebad41bb65e Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Sun, 3 Feb 2013 09:09:18 -0600
Subject: [PATCH 19/39] fixed issue 1315, IE9 layout, thanks Duffy Nicholas
---
VERSION | 2 +-
applications/welcome/views/layout.html | 13 +++++++------
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/VERSION b/VERSION
index e3791167..12aa908d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.01.21.23.40
+Version 2.4.1-alpha.2+timestamp.2013.02.03.09.08.28
diff --git a/applications/welcome/views/layout.html b/applications/welcome/views/layout.html
index 55e15b7b..16e5dc28 100644
--- a/applications/welcome/views/layout.html
+++ b/applications/welcome/views/layout.html
@@ -1,3 +1,4 @@
+
@@ -6,16 +7,16 @@
-
+{{=response.title or request.application}}
+
-
-
- {{=response.title or request.application}}
+
+
From d06a1a68a022af79b07ccb7be67d1554e7cd9fdc Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Sun, 3 Feb 2013 09:11:13 -0600
Subject: [PATCH 20/39] allow export/import of db models, issue 1316
(experimental), thanks Alan
---
VERSION | 2 +-
gluon/dal.py | 60 +++++++++++++++++++++++++++++++++-------------------
2 files changed, 39 insertions(+), 23 deletions(-)
diff --git a/VERSION b/VERSION
index 12aa908d..9c032e39 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.03.09.08.28
+Version 2.4.1-alpha.2+timestamp.2013.02.03.09.10.34
diff --git a/gluon/dal.py b/gluon/dal.py
index 4e3945a2..e4f08dc1 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -7124,8 +7124,12 @@ class DAL(object):
:attempts (defaults to 5). Number of times to attempt connecting
"""
+ dbdict = None
if uri == '' and db_uid is not None: return
-
+ elif isinstance(uri, dict):
+ dbdict = uri
+ uri = dbdict["uri"]
+ codec = dbdict["codec"] or codec
if not decode_credentials:
credential_decoder = lambda cred: cred
else:
@@ -7207,32 +7211,44 @@ class DAL(object):
self._fake_migrate = fake_migrate
self._migrate_enabled = migrate_enabled
self._fake_migrate_all = fake_migrate_all
- if auto_import:
- self.import_table_definitions(adapter.folder)
+ if auto_import or dbdict:
+ self.import_table_definitions(adapter.folder,
+ items=dbdict["items"])
@property
def tables(self):
return self._tables
- def import_table_definitions(self,path,migrate=False,fake_migrate=False):
+ def import_table_definitions(self, path, migrate=False,
+ fake_migrate=False, items=None):
pattern = pjoin(path,self._uri_hash+'_*.table')
- for filename in glob.glob(pattern):
- tfile = self._adapter.file_open(filename, 'r')
- try:
- sql_fields = pickle.load(tfile)
- name = filename[len(pattern)-7:-6]
- mf = [(value['sortable'],
- Field(key,
- type=value['type'],
- length=value.get('length',None),
- notnull=value.get('notnull',False),
- unique=value.get('unique',False))) \
- for key, value in sql_fields.iteritems()]
- mf.sort(lambda a,b: cmp(a[0],b[0]))
- self.define_table(name,*[item[1] for item in mf],
- **dict(migrate=migrate,fake_migrate=fake_migrate))
- finally:
- self._adapter.file_close(tfile)
+ if items:
+ for tablename, table in items.iteritems():
+ # TODO: read all field/table options
+ fields = []
+ for fieldname, field in table["items"].iteritems():
+ type = field["type"]
+ fields.append(Field(fieldname, type))
+ self.define_table(tablename, *fields)
+ else:
+ for filename in glob.glob(pattern):
+ tfile = self._adapter.file_open(filename, 'r')
+ try:
+ sql_fields = pickle.load(tfile)
+ name = filename[len(pattern)-7:-6]
+ mf = [(value['sortable'],
+ Field(key,
+ type=value['type'],
+ length=value.get('length',None),
+ notnull=value.get('notnull',False),
+ unique=value.get('unique',False))) \
+ for key, value in sql_fields.iteritems()]
+ mf.sort(lambda a,b: cmp(a[0],b[0]))
+ self.define_table(name,*[item[1] for item in mf],
+ **dict(migrate=migrate,
+ fake_migrate=fake_migrate))
+ finally:
+ self._adapter.file_close(tfile)
def check_reserved_keyword(self, name):
"""
@@ -7571,7 +7587,7 @@ def index():
def as_dict(self, flat=False, sanitize=True):
dbname = codec = uid = uri = None
if not sanitize:
- uri, dbname, codec, uid = (str(self), self._dbname,
+ uri, dbname, codec, uid = (self._uri, self._dbname,
self._db_codec, self._db_uid)
db_as_dict = dict(items={}, tables=[], uri=uri, dbname=dbname,
codec=codec, uid=uid)
From 90f8f223b9161f145dfaf23c2d793dd2c9e7f29c Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Mon, 4 Feb 2013 09:28:09 -0600
Subject: [PATCH 21/39] fixed issue 1318, as_yaml, thanks Alan
---
VERSION | 2 +-
gluon/dal.py | 19 +++++++++++++++++++
gluon/serializers.py | 16 ++++++++++++++++
3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/VERSION b/VERSION
index 9c032e39..de433d3e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.03.09.10.34
+Version 2.4.1-alpha.2+timestamp.2013.02.04.09.27.30
diff --git a/gluon/dal.py b/gluon/dal.py
index e4f08dc1..90537466 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -7610,6 +7610,12 @@ def index():
d = self.as_dict(flat=True, sanitize=sanitize)
return serializers.json(d)
+ def as_yaml(self, sanitize=True):
+ if not have_serializers:
+ raise ImportError("No YAML serializers available")
+ d = self.as_dict(flat=True, sanitize=sanitize)
+ return serializers.yaml(d)
+
def __contains__(self, tablename):
try:
return tablename in self.tables
@@ -8521,6 +8527,12 @@ class Table(object):
d = self.as_dict(flat=True, sanitize=sanitize)
return serializers.json(d)
+ def as_yaml(self, sanitize=True):
+ if not have_serializers:
+ raise ImportError("No YAML serializers available")
+ d = self.as_dict(flat=True, sanitize=sanitize)
+ return serializers.yaml(d)
+
def with_alias(self, alias):
return self._db._adapter.alias(self,alias)
@@ -9229,6 +9241,13 @@ class Field(Expression):
d = self.as_dict(flat=True, sanitize=sanitize)
return json(d)
+ def as_yaml(self, sanitize=True):
+ if have_serializers:
+ d = self.as_dict(flat=True, sanitize=sanitize)
+ return serializers.yaml(d)
+ else:
+ raise ImportError("No YAML serializers available")
+
def __nonzero__(self):
return True
diff --git a/gluon/serializers.py b/gluon/serializers.py
index e29061a1..8aece0fd 100644
--- a/gluon/serializers.py
+++ b/gluon/serializers.py
@@ -19,6 +19,11 @@ except ImportError:
except:
import contrib.simplejson as json_parser # fallback to pure-Python module
+have_yaml = True
+try:
+ import yaml
+except ImportError:
+ have_yaml = False
def loads_json(o):
# deserialize a json string
@@ -119,3 +124,14 @@ def rss(feed):
pubDate=entry.get('created_on', now)
) for entry in feed.get('entries', [])])
return rss.to_xml(encoding='utf-8')
+
+def yaml(data):
+ if have_yaml:
+ return yaml.dump(data)
+ else: raise ImportError("No YAML serializer available")
+
+def loads_yaml(data):
+ if have_yaml:
+ return yaml.load(data)
+ else: raise ImportError("No YAML serializer available")
+
From 7c1f6c2195e04294acab860ba77137e93b33fd4b Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Mon, 4 Feb 2013 09:37:44 -0600
Subject: [PATCH 22/39] optional_args
---
VERSION | 2 +-
gluon/dal.py | 9 +++++++--
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/VERSION b/VERSION
index de433d3e..012b9c25 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.04.09.27.30
+Version 2.4.1-alpha.2+timestamp.2013.02.04.09.37.03
diff --git a/gluon/dal.py b/gluon/dal.py
index 90537466..0d94769d 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -1328,10 +1328,11 @@ class BaseAdapter(ConnectionPool):
first = expression.first
second = expression.second
op = expression.op
+ optional_args = expression.optional_args or {}
if not second is None:
- return op(first, second)
+ return op(first, second, **optional_args)
elif not first is None:
- return op(first)
+ return op(first,**optional_args)
elif isinstance(op, str):
if op.endswith(';'):
op=op[:-1]
@@ -8560,6 +8561,7 @@ class Expression(object):
first=None,
second=None,
type=None,
+ **optional_args
):
self.db = db
@@ -8572,6 +8574,7 @@ class Expression(object):
self.type = first.type
else:
self.type = type
+ self.optional_args = optional_args
def sum(self):
db = self.db
@@ -9279,12 +9282,14 @@ class Query(object):
first=None,
second=None,
ignore_common_filters = False,
+ **optional_args
):
self.db = self._db = db
self.op = op
self.first = first
self.second = second
self.ignore_common_filters = ignore_common_filters
+ self.optional_args = optional_args
def __repr__(self):
return '' % BaseAdapter.expand(self.db._adapter,self)
From 6b9d7af4ed353f7e4272a9533ddb1e72d15cc1f1 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Mon, 4 Feb 2013 09:55:21 -0600
Subject: [PATCH 23/39] passing parameters for contains case_sensitive but
often ignored. :-(
---
VERSION | 2 +-
gluon/dal.py | 43 +++++++++++++++++++++++++++----------------
2 files changed, 28 insertions(+), 17 deletions(-)
diff --git a/VERSION b/VERSION
index 012b9c25..dc95465d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.04.09.37.03
+Version 2.4.1-alpha.2+timestamp.2013.02.04.09.54.44
diff --git a/gluon/dal.py b/gluon/dal.py
index 0d94769d..fd987234 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -1233,9 +1233,9 @@ class BaseAdapter(ConnectionPool):
return '(%s LIKE %s)' % (self.expand(first),
self.expand('%'+second, 'string'))
- def CONTAINS(self, first, second):
- field = self.expand(first)
+ def CONTAINS(self, first, second, case_sensitive=False):
if isinstance(second,Expression):
+ field = self.expand(first)
expr = self.expand(second,'string')
if first.type.startswith('list:'):
expr = 'CONCAT("|", %s, "|")' % expr
@@ -1249,7 +1249,8 @@ class BaseAdapter(ConnectionPool):
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
else:
raise RuntimeError("Expression Not Supported")
- return '(%s LIKE %s)' % (field,self.expand(key,'string'))
+ op = case_sensitive and self.LIKE or self.ILIKE
+ return op(first,key)
def EQ(self, first, second=None):
if second is None:
@@ -2627,12 +2628,13 @@ class PostgreSQLAdapter(BaseAdapter):
return '(%s ILIKE %s)' % (self.expand(first),
self.expand('%'+second,'string'))
- def CONTAINS(self,first,second):
+ def CONTAINS(self,first,second,case_sensitive=False):
if first.type in ('string','text', 'json'):
key = '%'+str(second).replace('%','%%')+'%'
elif first.type.startswith('list:'):
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
- return '(%s ILIKE %s)' % (self.expand(first),self.expand(key,'string'))
+ op = case_sensitive and self.LIKE or self.ILIKE
+ return op(first,key)
# GIS functions
@@ -3406,13 +3408,17 @@ class FireBirdAdapter(BaseAdapter):
def SUBSTRING(self,field,parameters):
return 'SUBSTRING(%s from %s for %s)' % (self.expand(field), parameters[0], parameters[1])
- def CONTAINS(self, first, second):
+ def CONTAINING(self,first,second):
+ "case in-sensitive like operator"
+ return '(%s CONTAINING %s)' % (self.expand(first),
+ self.expand(second, 'string'))
+
+ def CONTAINS(self, first, second, case_sensitive=False):
if first.type in ('string','text'):
key = str(second).replace('%','%%')
elif first.type.startswith('list:'):
key = '|'+str(second).replace('|','||').replace('%','%%')+'|'
- return '(%s CONTAINING %s)' % (self.expand(first),
- self.expand(key,'string'))
+ return self.CONTAINING(first,second)
def _drop(self,table,mode):
sequence_name = table._sequence_name
@@ -4572,7 +4578,8 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
second = [Key.from_path(first._tablename, int(i)) for i in second]
return [GAEF(first.name,'in',second,lambda a,b:a in b)]
- def CONTAINS(self,first,second):
+ def CONTAINS(self,first,second,case_sensitive=False):
+ # silently ignoring: GAE can only do case sensitive matches!
if not first.type.startswith('list:'):
raise SyntaxError("Not supported")
return [GAEF(first.name,'=',self.expand(second,first.type[5:]),lambda a,b:b in a)]
@@ -5525,8 +5532,9 @@ class MongoDBAdapter(NoSQLAdapter):
return {self.expand(first): ('/%s^/' % \
self.expand(second, 'string'))}
- def CONTAINS(self, first, second):
- #There is a technical difference, but mongodb doesn't support
+ def CONTAINS(self, first, second, case_sensitive=False):
+ # silently ignore, only case sensitive
+ # There is a technical difference, but mongodb doesn't support
# that, but the result will be the same
return {self.expand(first) : ('/%s/' % \
self.expand(second, 'string'))}
@@ -5556,7 +5564,8 @@ class MongoDBAdapter(NoSQLAdapter):
re.escape(self.expand(second, 'string')) + '$'}}
#TODO verify full compatibilty with official oracle contains operator
- def CONTAINS(self, first, second):
+ def CONTAINS(self, first, second, case_sensitive=False):
+ # silently ignore, only case sensitive
#There is a technical difference, but mongodb doesn't support
# that, but the result will be the same
#TODO contains operators need to be transformed to Regex
@@ -6346,7 +6355,8 @@ class IMAPAdapter(NoSQLAdapter):
# result = "(%s %s)" % (self.expand(first), self.expand(second))
return result
- def CONTAINS(self, first, second):
+ def CONTAINS(self, first, second, case_sensitive=False):
+ # silently ignore, only case sensitive
result = None
name = self.search_fields[first.name]
@@ -8764,17 +8774,18 @@ class Expression(object):
raise SyntaxError("endswith used with incompatible field type")
return Query(db, db._adapter.ENDSWITH, self, value)
- def contains(self, value, all=False):
+ def contains(self, value, all=False, case_sensitive=False):
db = self.db
if isinstance(value,(list, tuple)):
- subqueries = [self.contains(str(v).strip()) for v in value if str(v).strip()]
+ subqueries = [self.contains(str(v).strip(),case_sensitive=case_sensitive)
+ for v in value if str(v).strip()]
if not subqueries:
return self.contains('')
else:
return reduce(all and AND or OR,subqueries)
if not self.type in ('string', 'text', 'json') and not self.type.startswith('list:'):
raise SyntaxError("contains used with incompatible field type")
- return Query(db, db._adapter.CONTAINS, self, value)
+ return Query(db, db._adapter.CONTAINS, self, value, case_sensitive=case_sensitive)
def with_alias(self, alias):
db = self.db
From 4fb833329916d2667714586d96162c99d0fd2615 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Mon, 4 Feb 2013 10:01:00 -0600
Subject: [PATCH 24/39] added comments to explain caveats with last commit
---
VERSION | 2 +-
gluon/dal.py | 5 +++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/VERSION b/VERSION
index dc95465d..3e8d8717 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.04.09.54.44
+Version 2.4.1-alpha.2+timestamp.2013.02.04.10.00.20
diff --git a/gluon/dal.py b/gluon/dal.py
index fd987234..c714556d 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -8775,6 +8775,11 @@ class Expression(object):
return Query(db, db._adapter.ENDSWITH, self, value)
def contains(self, value, all=False, case_sensitive=False):
+ """
+ The case_sensitive parameters is only useful for PostgreSQL
+ For other RDMBs it is ignored and contains is always case in-sensitive
+ For MongoDB and GAE contains is always case sensitive
+ """
db = self.db
if isinstance(value,(list, tuple)):
subqueries = [self.contains(str(v).strip(),case_sensitive=case_sensitive)
From 8f76efeb2f29c525086172c8c570e82499e1db83 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Mon, 4 Feb 2013 10:03:29 -0600
Subject: [PATCH 25/39] fixed issue 1320
(http://timelessrepo.com/json-isnt-a-javascript-subset), thanks Alan
---
VERSION | 2 +-
gluon/serializers.py | 8 ++++++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/VERSION b/VERSION
index 3e8d8717..09d2eb37 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.04.10.00.20
+Version 2.4.1-alpha.2+timestamp.2013.02.04.10.02.48
diff --git a/gluon/serializers.py b/gluon/serializers.py
index 8aece0fd..1d2621d2 100644
--- a/gluon/serializers.py
+++ b/gluon/serializers.py
@@ -73,8 +73,12 @@ def xml(value, encoding='UTF-8', key='document', quote=True):
def json(value, default=custom_json):
- return json_parser.dumps(value, default=default)
-
+ # replace JavaScript incompatible spacing
+ # http://timelessrepo.com/json-isnt-a-javascript-subset
+ return json_parser.dumps(value,
+ default=default).replace(ur'\u2028',
+ '\\u2028').replace(ur'\2029',
+ '\\u2029')
def csv(value):
return ''
From 9069f0685717c30c2a7e32a344702a34ac470ec7 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Mon, 4 Feb 2013 15:12:15 -0600
Subject: [PATCH 26/39] fixed cache expiration in grid downloads, thanks
Niphlod
---
VERSION | 2 +-
gluon/dal.py | 32 ++++++++++++++++----------------
gluon/globals.py | 8 ++++----
gluon/streamer.py | 4 ++--
4 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/VERSION b/VERSION
index 09d2eb37..23ab0425 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.04.10.02.48
+Version 2.4.1-alpha.2+timestamp.2013.02.04.15.11.29
diff --git a/gluon/dal.py b/gluon/dal.py
index c714556d..6be400b8 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -876,7 +876,7 @@ class BaseAdapter(ConnectionPool):
# The reason is that we do not want to trigger
# a migration simply because a default value changes.
not_null = self.NOT_NULL(field.default, field_type)
- ftype = ftype.replace('NOT NULL', not_null)
+ ftype = ftype.replace('NOT NULL', not_null)
sql_fields_aux[field_name] = dict(sql=ftype)
# Postgres - PostGIS:
# geometry fields are added after the table has been created, not now
@@ -1234,7 +1234,7 @@ class BaseAdapter(ConnectionPool):
self.expand('%'+second, 'string'))
def CONTAINS(self, first, second, case_sensitive=False):
- if isinstance(second,Expression):
+ if isinstance(second,Expression):
field = self.expand(first)
expr = self.expand(second,'string')
if first.type.startswith('list:'):
@@ -3265,7 +3265,7 @@ class MSSQL2Adapter(MSSQLAdapter):
def execute(self,a):
return self.log_execute(a.decode('utf8'))
-
+
class SybaseAdapter(MSSQLAdapter):
drivers = ('Sybase',)
@@ -5037,7 +5037,7 @@ class MongoDBAdapter(NoSQLAdapter):
'list:reference': list,
}
- error_messages = {"javascript_needed": "This must yet be replaced" +
+ error_messages = {"javascript_needed": "This must yet be replaced" +
" with javascript in order to work."}
def __init__(self,db,uri='mongodb://127.0.0.1:5984/db',
@@ -5116,12 +5116,12 @@ class MongoDBAdapter(NoSQLAdapter):
raise ValueError(
"invalid objectid argument string: %s" % e)
else:
- raise ValueError("Invalid objectid argument string. " +
+ raise ValueError("Invalid objectid argument string. " +
"Requires an integer or base 16 value")
elif isinstance(arg, self.ObjectId):
return arg
if not isinstance(arg, (int, long)):
- raise TypeError("object_id argument must be of type " +
+ raise TypeError("object_id argument must be of type " +
"ObjectId or an objectid representable integer")
if arg == 0:
hexvalue = "".zfill(24)
@@ -5153,7 +5153,7 @@ class MongoDBAdapter(NoSQLAdapter):
return value
return value
- # Safe determines whether a asynchronious request is done or a
+ # Safe determines whether a asynchronious request is done or a
# synchronious action is done
# For safety, we use by default synchronious requests
def insert(self, table, fields, safe=None):
@@ -5273,7 +5273,7 @@ class MongoDBAdapter(NoSQLAdapter):
elif len(fields) != 0:
tablename = fields[0].tablename
else:
- raise SyntaxError("The table name could not be found in " +
+ raise SyntaxError("The table name could not be found in " +
"the query nor from the select statement.")
mongoqry_dict = self.expand(query)
@@ -5299,7 +5299,7 @@ class MongoDBAdapter(NoSQLAdapter):
sort=mongosort_list, snapshot=snapshot).count()}
else:
# pymongo cursor object
- mongo_list_dicts = ctable.find(mongoqry_dict,
+ mongo_list_dicts = ctable.find(mongoqry_dict,
mongofields_dict, skip=limitby_skip,
limit=limitby_limit, sort=mongosort_list,
snapshot=snapshot)
@@ -5396,7 +5396,7 @@ class MongoDBAdapter(NoSQLAdapter):
if safe is None:
safe = self.safe
amount = 0
- amount = self.count(query, False)
+ amount = self.count(query, False)
if not isinstance(query, Query):
raise RuntimeError("query type %s is not supported" % \
type(query))
@@ -5695,7 +5695,7 @@ class IMAPAdapter(NoSQLAdapter):
# This avoids the extra server names retrieval
imapdb.define_tables({"inbox": "INBOX"})
-
+
# Selects without content/attachments/email columns will only
# fetch header and flags
@@ -6088,7 +6088,7 @@ class IMAPAdapter(NoSQLAdapter):
# keep the requests small for header/flags
if any([(field.name in ["content", "size",
- "attachments", "email"]) for
+ "attachments", "email"]) for
field in fields]):
imap_fields = "(RFC822 FLAGS)"
else:
@@ -7293,14 +7293,14 @@ def index():
"/{person.name}/pets[pet.ownedby]/{pet.name}",
"/{person.name}/pets[pet.ownedby]/{pet.name}/:field",
("/dogs[pet]", db.pet.info=='dog'),
- ("/dogs[pet]/{pet.name.startswith}", db.pet.info=='dog'),
+ ("/dogs[pet]/{pet.name.startswith}", db.pet.info=='dog'),
]
parser = db.parse_as_rest(patterns,args,vars)
if parser.status == 200:
return dict(content=parser.response)
else:
raise HTTP(parser.status,parser.error)
-
+
def POST(table_name,**vars):
if table_name == 'person':
return db.person.validate_and_insert(**vars)
@@ -8797,7 +8797,7 @@ class Expression(object):
return Expression(db, db._adapter.AS, self, alias, self.type)
# GIS expressions
-
+
def st_asgeojson(self, precision=15, options=0, version=1):
return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self,
dict(precision=precision, options=options,
@@ -9131,7 +9131,7 @@ class Field(Expression):
stream = self.uploadfs.open(name, 'rb')
else:
# ## if file is on regular filesystem
- stream = open(pjoin(file_properties['path'], name), 'rb')
+ stream = pjoin(file_properties['path'], name)
return (filename, stream)
def retrieve_file_properties(self, name, path=None):
diff --git a/gluon/globals.py b/gluon/globals.py
index 11f3c7e5..09397aa5 100644
--- a/gluon/globals.py
+++ b/gluon/globals.py
@@ -308,8 +308,8 @@ class Response(Storage):
chunk_size=DEFAULT_CHUNK_SIZE,
request=None,
attachment=False,
- filename=None,
- ):
+ filename=None
+ ):
"""
if a controller function::
@@ -403,8 +403,8 @@ class Response(Storage):
raise HTTP(404)
headers = self.headers
headers['Content-Type'] = contenttype(name)
- if download_filename == None:
- download_filename = filename
+ if download_filename == None:
+ download_filename = filename
if attachment:
headers['Content-Disposition'] = \
'attachment; filename="%s"' % download_filename.replace('"','\"')
diff --git a/gluon/streamer.py b/gluon/streamer.py
index 635deecd..f2194715 100644
--- a/gluon/streamer.py
+++ b/gluon/streamer.py
@@ -46,8 +46,8 @@ def stream_file_or_304_or_206(
request=None,
headers={},
status=200,
- error_message=None,
-):
+ error_message=None
+ ):
if error_message is None:
error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
try:
From ba603528d78687b92f051bf438c6e0665aafc8d1 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Mon, 4 Feb 2013 16:45:42 -0600
Subject: [PATCH 27/39] grid breadcrumbs uses field.label, not field.name,
thanks Michael Beller
---
VERSION | 2 +-
gluon/sqlhtml.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index 23ab0425..6533dea2 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.04.15.11.29
+Version 2.4.1-alpha.2+timestamp.2013.02.04.16.44.47
diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py
index 90ccf722..750df107 100644
--- a/gluon/sqlhtml.py
+++ b/gluon/sqlhtml.py
@@ -2552,7 +2552,7 @@ class SQLFORM(FORM):
links_in_grid=links_in_grid,
user_signature=user_signature, **kwargs)
if isinstance(grid, DIV):
- header = table._plural + (field and ' for ' + field.name or '')
+ header = table._plural + (field and ' for ' + field.label or '')
breadcrumbs.append(LI(A(T(header), _class=trap_class(),
_href=url()), _class='active w2p_grid_breadcrumb_elem'))
grid.insert(
From cbb9a1fdbeb08b116431ddac594c48fcd4da424f Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Tue, 5 Feb 2013 08:46:32 -0600
Subject: [PATCH 28/39] added pypyodbc.py, thanks Derek
---
VERSION | 2 +-
gluon/contrib/pypyodbc.py | 2338 +++++++++++++++++++++++++++++++++++++
gluon/dal.py | 3 +
3 files changed, 2342 insertions(+), 1 deletion(-)
create mode 100644 gluon/contrib/pypyodbc.py
diff --git a/VERSION b/VERSION
index 6533dea2..6a617ef7 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.04.16.44.47
+Version 2.4.1-alpha.2+timestamp.2013.02.05.08.45.51
diff --git a/gluon/contrib/pypyodbc.py b/gluon/contrib/pypyodbc.py
new file mode 100644
index 00000000..d151ad25
--- /dev/null
+++ b/gluon/contrib/pypyodbc.py
@@ -0,0 +1,2338 @@
+# -*- coding: utf-8 -*-
+
+# PyPyODBC is develped from RealPyODBC 0.1 beta released in 2004 by Michele Petrazzo. Thanks Michele.
+
+# The MIT License (MIT)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+# documentation files (the "Software"), to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all copies or substantial portions
+# of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO #EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from __future__ import with_statement
+
+import sys, os, datetime, ctypes, threading
+from decimal import Decimal
+
+try:
+ bytearray
+except NameError:
+ # pre version 2.6 python does not have the bytearray type
+ bytearray = str
+
+if not hasattr(ctypes, 'c_ssize_t'):
+ if ctypes.sizeof(ctypes.c_uint) == ctypes.sizeof(ctypes.c_void_p):
+ ctypes.c_ssize_t = ctypes.c_int
+ elif ctypes.sizeof(ctypes.c_ulong) == ctypes.sizeof(ctypes.c_void_p):
+ ctypes.c_ssize_t = ctypes.c_long
+ elif ctypes.sizeof(ctypes.c_ulonglong) == ctypes.sizeof(ctypes.c_void_p):
+ ctypes.c_ssize_t = ctypes.c_longlong
+
+DEBUG = 0
+# Comment out all "if DEBUG:" statements like below for production
+if DEBUG: print 'DEBUGGING'
+
+pooling = True
+lock = threading.Lock()
+shared_env_h = None
+apilevel = '2.0'
+paramstyle = 'qmark'
+threadsafety = 1
+version = '0.9.1'
+lowercase=True
+SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_wchar)
+
+#determin the size of Py_UNICODE
+#sys.maxunicode > 65536 and 'UCS4' or 'UCS2'
+UNICODE_SIZE = sys.maxunicode > 65536 and 4 or 2
+
+
+# Define ODBC constants. They are widly used in ODBC documents and programs
+# They are defined in cpp header files: sql.h sqlext.h sqltypes.h sqlucode.h
+# and you can get these files from the mingw32-runtime_3.13-1_all.deb package
+SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC2, SQL_OV_ODBC3 = 200, 2, 3
+SQL_DRIVER_NOPROMPT = 0
+SQL_ATTR_CONNECTION_POOLING = 201; SQL_CP_ONE_PER_HENV = 2
+
+SQL_FETCH_NEXT, SQL_FETCH_FIRST, SQL_FETCH_LAST = 0x01, 0x02, 0x04
+SQL_NULL_HANDLE, SQL_HANDLE_ENV, SQL_HANDLE_DBC, SQL_HANDLE_STMT = 0, 1, 2, 3
+SQL_SUCCESS, SQL_SUCCESS_WITH_INFO = 0, 1
+SQL_NO_DATA = 100; SQL_NO_TOTAL = -4
+SQL_ATTR_ACCESS_MODE = SQL_ACCESS_MODE = 101
+SQL_ATTR_AUTOCOMMIT = SQL_AUTOCOMMIT = 102
+
+SQL_MODE_DEFAULT = SQL_MODE_READ_WRITE = 0; SQL_MODE_READ_ONLY = 1
+SQL_AUTOCOMMIT_OFF, SQL_AUTOCOMMIT_ON = 0, 1
+SQL_IS_UINTEGER = -5
+SQL_ATTR_LOGIN_TIMEOUT = 103; SQL_ATTR_CONNECTION_TIMEOUT = 113
+SQL_COMMIT, SQL_ROLLBACK = 0, 1
+
+SQL_INDEX_UNIQUE,SQL_INDEX_ALL = 0,1
+SQL_QUICK,SQL_ENSURE = 0,1
+SQL_FETCH_NEXT = 1
+SQL_COLUMN_DISPLAY_SIZE = 6
+SQL_INVALID_HANDLE = -2
+SQL_NO_DATA_FOUND = 100; SQL_NULL_DATA = -1; SQL_NTS = -3
+SQL_HANDLE_DESCR = 4
+SQL_TABLE_NAMES = 3
+SQL_PARAM_INPUT = 1; SQL_PARAM_INPUT_OUTPUT = 2
+SQL_PARAM_TYPE_UNKNOWN = 0
+SQL_RESULT_COL = 3
+SQL_PARAM_OUTPUT = 4
+SQL_RETURN_VALUE = 5
+SQL_PARAM_TYPE_DEFAULT = SQL_PARAM_INPUT_OUTPUT
+
+SQL_RESET_PARAMS = 3
+SQL_UNBIND = 2
+SQL_CLOSE = 0
+
+SQL_TYPE_NULL = 0
+SQL_DECIMAL = 3
+SQL_FLOAT = 6
+SQL_DATE = 9
+SQL_TIME = 10
+SQL_TIMESTAMP = 11
+SQL_VARCHAR = 12
+SQL_LONGVARCHAR = -1
+SQL_VARBINARY = -3
+SQL_LONGVARBINARY = -4
+SQL_BIGINT = -5
+SQL_WVARCHAR = -9
+SQL_WLONGVARCHAR = -10
+SQL_ALL_TYPES = 0
+SQL_SIGNED_OFFSET = -20
+
+SQL_C_CHAR = SQL_CHAR = 1
+SQL_C_NUMERIC = SQL_NUMERIC = 2
+SQL_C_LONG = SQL_INTEGER = 4
+SQL_C_SLONG = SQL_C_LONG + SQL_SIGNED_OFFSET
+SQL_C_SHORT = SQL_SMALLINT = 5
+SQL_C_FLOAT = SQL_REAL = 7
+SQL_C_DOUBLE = SQL_DOUBLE = 8
+SQL_C_TYPE_DATE = SQL_TYPE_DATE = 91
+SQL_C_TYPE_TIME = SQL_TYPE_TIME = 92
+SQL_C_BINARY = SQL_BINARY = -2
+SQL_C_SBIGINT = SQL_BIGINT + SQL_SIGNED_OFFSET
+SQL_C_TINYINT = SQL_TINYINT = -6
+SQL_C_BIT = SQL_BIT = -7
+SQL_C_WCHAR = SQL_WCHAR = -8
+SQL_C_GUID = SQL_GUID = -11
+SQL_C_TYPE_TIMESTAMP = SQL_TYPE_TIMESTAMP = 93
+SQL_C_DEFAULT = 99
+
+SQL_SS_TIME2 = -154
+
+SQL_DESC_DISPLAY_SIZE = SQL_COLUMN_DISPLAY_SIZE
+
+
+def dttm_cvt(x):
+ if x == '': return None
+ else: return datetime.datetime(int(x[0:4]),int(x[5:7]),int(x[8:10]),int(x[10:13]),int(x[14:16]),int(x[17:19]),int(x[20:].ljust(6,'0')))
+
+def tm_cvt(x):
+ if x == '': return None
+ else: return datetime.time(int(x[0:2]),int(x[3:5]),int(x[6:8]),int(x[9:].ljust(6,'0')))
+
+def dt_cvt(x):
+ if x == '': return None
+ else: return datetime.date(int(x[0:4]),int(x[5:7]),int(x[8:10]))
+
+
+# Below defines The constants for sqlgetinfo method, and their coresponding return types
+SQL_QUALIFIER_LOCATION = 114
+SQL_QUALIFIER_NAME_SEPARATOR = 41
+SQL_QUALIFIER_TERM = 42
+SQL_QUALIFIER_USAGE = 92
+SQL_OWNER_TERM = 39
+SQL_OWNER_USAGE = 91
+SQL_ACCESSIBLE_PROCEDURES = 20
+SQL_ACCESSIBLE_TABLES = 19
+SQL_ACTIVE_ENVIRONMENTS = 116
+SQL_AGGREGATE_FUNCTIONS = 169
+SQL_ALTER_DOMAIN = 117
+SQL_ALTER_TABLE = 86
+SQL_ASYNC_MODE = 10021
+SQL_BATCH_ROW_COUNT = 120
+SQL_BATCH_SUPPORT = 121
+SQL_BOOKMARK_PERSISTENCE = 82
+SQL_CATALOG_LOCATION = SQL_QUALIFIER_LOCATION
+SQL_CATALOG_NAME = 10003
+SQL_CATALOG_NAME_SEPARATOR = SQL_QUALIFIER_NAME_SEPARATOR
+SQL_CATALOG_TERM = SQL_QUALIFIER_TERM
+SQL_CATALOG_USAGE = SQL_QUALIFIER_USAGE
+SQL_COLLATION_SEQ = 10004
+SQL_COLUMN_ALIAS = 87
+SQL_CONCAT_NULL_BEHAVIOR = 22
+SQL_CONVERT_FUNCTIONS = 48
+SQL_CONVERT_VARCHAR = 70
+SQL_CORRELATION_NAME = 74
+SQL_CREATE_ASSERTION = 127
+SQL_CREATE_CHARACTER_SET = 128
+SQL_CREATE_COLLATION = 129
+SQL_CREATE_DOMAIN = 130
+SQL_CREATE_SCHEMA = 131
+SQL_CREATE_TABLE = 132
+SQL_CREATE_TRANSLATION = 133
+SQL_CREATE_VIEW = 134
+SQL_CURSOR_COMMIT_BEHAVIOR = 23
+SQL_CURSOR_ROLLBACK_BEHAVIOR = 24
+SQL_DATABASE_NAME = 16
+SQL_DATA_SOURCE_NAME = 2
+SQL_DATA_SOURCE_READ_ONLY = 25
+SQL_DATETIME_LITERALS = 119
+SQL_DBMS_NAME = 17
+SQL_DBMS_VER = 18
+SQL_DDL_INDEX = 170
+SQL_DEFAULT_TXN_ISOLATION = 26
+SQL_DESCRIBE_PARAMETER = 10002
+SQL_DM_VER = 171
+SQL_DRIVER_NAME = 6
+SQL_DRIVER_ODBC_VER = 77
+SQL_DRIVER_VER = 7
+SQL_DROP_ASSERTION = 136
+SQL_DROP_CHARACTER_SET = 137
+SQL_DROP_COLLATION = 138
+SQL_DROP_DOMAIN = 139
+SQL_DROP_SCHEMA = 140
+SQL_DROP_TABLE = 141
+SQL_DROP_TRANSLATION = 142
+SQL_DROP_VIEW = 143
+SQL_DYNAMIC_CURSOR_ATTRIBUTES1 = 144
+SQL_DYNAMIC_CURSOR_ATTRIBUTES2 = 145
+SQL_EXPRESSIONS_IN_ORDERBY = 27
+SQL_FILE_USAGE = 84
+SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 = 146
+SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 = 147
+SQL_GETDATA_EXTENSIONS = 81
+SQL_GROUP_BY = 88
+SQL_IDENTIFIER_CASE = 28
+SQL_IDENTIFIER_QUOTE_CHAR = 29
+SQL_INDEX_KEYWORDS = 148
+SQL_INFO_SCHEMA_VIEWS = 149
+SQL_INSERT_STATEMENT = 172
+SQL_INTEGRITY = 73
+SQL_KEYSET_CURSOR_ATTRIBUTES1 = 150
+SQL_KEYSET_CURSOR_ATTRIBUTES2 = 151
+SQL_KEYWORDS = 89
+SQL_LIKE_ESCAPE_CLAUSE = 113
+SQL_MAX_ASYNC_CONCURRENT_STATEMENTS = 10022
+SQL_MAX_BINARY_LITERAL_LEN = 112
+SQL_MAX_CATALOG_NAME_LEN = 34
+SQL_MAX_CHAR_LITERAL_LEN = 108
+SQL_MAX_COLUMNS_IN_GROUP_BY = 97
+SQL_MAX_COLUMNS_IN_INDEX = 98
+SQL_MAX_COLUMNS_IN_ORDER_BY = 99
+SQL_MAX_COLUMNS_IN_SELECT = 100
+SQL_MAX_COLUMNS_IN_TABLE = 101
+SQL_MAX_COLUMN_NAME_LEN = 30
+SQL_MAX_CONCURRENT_ACTIVITIES = 1
+SQL_MAX_CURSOR_NAME_LEN = 31
+SQL_MAX_DRIVER_CONNECTIONS = 0
+SQL_MAX_IDENTIFIER_LEN = 10005
+SQL_MAX_INDEX_SIZE = 102
+SQL_MAX_PROCEDURE_NAME_LEN = 33
+SQL_MAX_ROW_SIZE = 104
+SQL_MAX_ROW_SIZE_INCLUDES_LONG = 103
+SQL_MAX_SCHEMA_NAME_LEN = 32
+SQL_MAX_STATEMENT_LEN = 105
+SQL_MAX_TABLES_IN_SELECT = 106
+SQL_MAX_TABLE_NAME_LEN = 35
+SQL_MAX_USER_NAME_LEN = 107
+SQL_MULTIPLE_ACTIVE_TXN = 37
+SQL_MULT_RESULT_SETS = 36
+SQL_NEED_LONG_DATA_LEN = 111
+SQL_NON_NULLABLE_COLUMNS = 75
+SQL_NULL_COLLATION = 85
+SQL_NUMERIC_FUNCTIONS = 49
+SQL_ODBC_INTERFACE_CONFORMANCE = 152
+SQL_ODBC_VER = 10
+SQL_OJ_CAPABILITIES = 65003
+SQL_ORDER_BY_COLUMNS_IN_SELECT = 90
+SQL_PARAM_ARRAY_ROW_COUNTS = 153
+SQL_PARAM_ARRAY_SELECTS = 154
+SQL_PROCEDURES = 21
+SQL_PROCEDURE_TERM = 40
+SQL_QUOTED_IDENTIFIER_CASE = 93
+SQL_ROW_UPDATES = 11
+SQL_SCHEMA_TERM = SQL_OWNER_TERM
+SQL_SCHEMA_USAGE = SQL_OWNER_USAGE
+SQL_SCROLL_OPTIONS = 44
+SQL_SEARCH_PATTERN_ESCAPE = 14
+SQL_SERVER_NAME = 13
+SQL_SPECIAL_CHARACTERS = 94
+SQL_SQL92_DATETIME_FUNCTIONS = 155
+SQL_SQL92_FOREIGN_KEY_DELETE_RULE = 156
+SQL_SQL92_FOREIGN_KEY_UPDATE_RULE = 157
+SQL_SQL92_GRANT = 158
+SQL_SQL92_NUMERIC_VALUE_FUNCTIONS = 159
+SQL_SQL92_PREDICATES = 160
+SQL_SQL92_RELATIONAL_JOIN_OPERATORS = 161
+SQL_SQL92_REVOKE = 162
+SQL_SQL92_ROW_VALUE_CONSTRUCTOR = 163
+SQL_SQL92_STRING_FUNCTIONS = 164
+SQL_SQL92_VALUE_EXPRESSIONS = 165
+SQL_SQL_CONFORMANCE = 118
+SQL_STANDARD_CLI_CONFORMANCE = 166
+SQL_STATIC_CURSOR_ATTRIBUTES1 = 167
+SQL_STATIC_CURSOR_ATTRIBUTES2 = 168
+SQL_STRING_FUNCTIONS = 50
+SQL_SUBQUERIES = 95
+SQL_SYSTEM_FUNCTIONS = 51
+SQL_TABLE_TERM = 45
+SQL_TIMEDATE_ADD_INTERVALS = 109
+SQL_TIMEDATE_DIFF_INTERVALS = 110
+SQL_TIMEDATE_FUNCTIONS = 52
+SQL_TXN_CAPABLE = 46
+SQL_TXN_ISOLATION_OPTION = 72
+SQL_UNION = 96
+SQL_USER_NAME = 47
+SQL_XOPEN_CLI_YEAR = 10000
+
+
+aInfoTypes = {
+SQL_ACCESSIBLE_PROCEDURES : 'GI_YESNO',SQL_ACCESSIBLE_TABLES : 'GI_YESNO',SQL_ACTIVE_ENVIRONMENTS : 'GI_USMALLINT',
+SQL_AGGREGATE_FUNCTIONS : 'GI_UINTEGER',SQL_ALTER_DOMAIN : 'GI_UINTEGER',
+SQL_ALTER_TABLE : 'GI_UINTEGER',SQL_ASYNC_MODE : 'GI_UINTEGER',SQL_BATCH_ROW_COUNT : 'GI_UINTEGER',
+SQL_BATCH_SUPPORT : 'GI_UINTEGER',SQL_BOOKMARK_PERSISTENCE : 'GI_UINTEGER',SQL_CATALOG_LOCATION : 'GI_USMALLINT',
+SQL_CATALOG_NAME : 'GI_YESNO',SQL_CATALOG_NAME_SEPARATOR : 'GI_STRING',SQL_CATALOG_TERM : 'GI_STRING',
+SQL_CATALOG_USAGE : 'GI_UINTEGER',SQL_COLLATION_SEQ : 'GI_STRING',SQL_COLUMN_ALIAS : 'GI_YESNO',
+SQL_CONCAT_NULL_BEHAVIOR : 'GI_USMALLINT',SQL_CONVERT_FUNCTIONS : 'GI_UINTEGER',
+SQL_CONVERT_VARCHAR : 'GI_UINTEGER',SQL_CORRELATION_NAME : 'GI_USMALLINT',
+SQL_CREATE_ASSERTION : 'GI_UINTEGER',SQL_CREATE_CHARACTER_SET : 'GI_UINTEGER',
+SQL_CREATE_COLLATION : 'GI_UINTEGER',SQL_CREATE_DOMAIN : 'GI_UINTEGER',SQL_CREATE_SCHEMA : 'GI_UINTEGER',
+SQL_CREATE_TABLE : 'GI_UINTEGER',SQL_CREATE_TRANSLATION : 'GI_UINTEGER',SQL_CREATE_VIEW : 'GI_UINTEGER',
+SQL_CURSOR_COMMIT_BEHAVIOR : 'GI_USMALLINT',SQL_CURSOR_ROLLBACK_BEHAVIOR : 'GI_USMALLINT',SQL_DATABASE_NAME : 'GI_STRING',
+SQL_DATA_SOURCE_NAME : 'GI_STRING',SQL_DATA_SOURCE_READ_ONLY : 'GI_YESNO',SQL_DATETIME_LITERALS : 'GI_UINTEGER',
+SQL_DBMS_NAME : 'GI_STRING',SQL_DBMS_VER : 'GI_STRING',SQL_DDL_INDEX : 'GI_UINTEGER',
+SQL_DEFAULT_TXN_ISOLATION : 'GI_UINTEGER',SQL_DESCRIBE_PARAMETER : 'GI_YESNO',SQL_DM_VER : 'GI_STRING',
+SQL_DRIVER_NAME : 'GI_STRING',SQL_DRIVER_ODBC_VER : 'GI_STRING',SQL_DRIVER_VER : 'GI_STRING',
+SQL_DROP_ASSERTION : 'GI_UINTEGER',SQL_DROP_CHARACTER_SET : 'GI_UINTEGER',
+SQL_DROP_COLLATION : 'GI_UINTEGER',SQL_DROP_DOMAIN : 'GI_UINTEGER',
+SQL_DROP_SCHEMA : 'GI_UINTEGER',SQL_DROP_TABLE : 'GI_UINTEGER',SQL_DROP_TRANSLATION : 'GI_UINTEGER',
+SQL_DROP_VIEW : 'GI_UINTEGER',SQL_DYNAMIC_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_DYNAMIC_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',
+SQL_EXPRESSIONS_IN_ORDERBY : 'GI_YESNO',SQL_FILE_USAGE : 'GI_USMALLINT',
+SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',
+SQL_GETDATA_EXTENSIONS : 'GI_UINTEGER',SQL_GROUP_BY : 'GI_USMALLINT',SQL_IDENTIFIER_CASE : 'GI_USMALLINT',
+SQL_IDENTIFIER_QUOTE_CHAR : 'GI_STRING',SQL_INDEX_KEYWORDS : 'GI_UINTEGER',SQL_INFO_SCHEMA_VIEWS : 'GI_UINTEGER',
+SQL_INSERT_STATEMENT : 'GI_UINTEGER',SQL_INTEGRITY : 'GI_YESNO',SQL_KEYSET_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',
+SQL_KEYSET_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',SQL_KEYWORDS : 'GI_STRING',
+SQL_LIKE_ESCAPE_CLAUSE : 'GI_YESNO',SQL_MAX_ASYNC_CONCURRENT_STATEMENTS : 'GI_UINTEGER',
+SQL_MAX_BINARY_LITERAL_LEN : 'GI_UINTEGER',SQL_MAX_CATALOG_NAME_LEN : 'GI_USMALLINT',
+SQL_MAX_CHAR_LITERAL_LEN : 'GI_UINTEGER',SQL_MAX_COLUMNS_IN_GROUP_BY : 'GI_USMALLINT',
+SQL_MAX_COLUMNS_IN_INDEX : 'GI_USMALLINT',SQL_MAX_COLUMNS_IN_ORDER_BY : 'GI_USMALLINT',
+SQL_MAX_COLUMNS_IN_SELECT : 'GI_USMALLINT',SQL_MAX_COLUMNS_IN_TABLE : 'GI_USMALLINT',
+SQL_MAX_COLUMN_NAME_LEN : 'GI_USMALLINT',SQL_MAX_CONCURRENT_ACTIVITIES : 'GI_USMALLINT',
+SQL_MAX_CURSOR_NAME_LEN : 'GI_USMALLINT',SQL_MAX_DRIVER_CONNECTIONS : 'GI_USMALLINT',
+SQL_MAX_IDENTIFIER_LEN : 'GI_USMALLINT',SQL_MAX_INDEX_SIZE : 'GI_UINTEGER',
+SQL_MAX_PROCEDURE_NAME_LEN : 'GI_USMALLINT',SQL_MAX_ROW_SIZE : 'GI_UINTEGER',
+SQL_MAX_ROW_SIZE_INCLUDES_LONG : 'GI_YESNO',SQL_MAX_SCHEMA_NAME_LEN : 'GI_USMALLINT',
+SQL_MAX_STATEMENT_LEN : 'GI_UINTEGER',SQL_MAX_TABLES_IN_SELECT : 'GI_USMALLINT',
+SQL_MAX_TABLE_NAME_LEN : 'GI_USMALLINT',SQL_MAX_USER_NAME_LEN : 'GI_USMALLINT',
+SQL_MULTIPLE_ACTIVE_TXN : 'GI_YESNO',SQL_MULT_RESULT_SETS : 'GI_YESNO',
+SQL_NEED_LONG_DATA_LEN : 'GI_YESNO',SQL_NON_NULLABLE_COLUMNS : 'GI_USMALLINT',
+SQL_NULL_COLLATION : 'GI_USMALLINT',SQL_NUMERIC_FUNCTIONS : 'GI_UINTEGER',
+SQL_ODBC_INTERFACE_CONFORMANCE : 'GI_UINTEGER',SQL_ODBC_VER : 'GI_STRING',SQL_OJ_CAPABILITIES : 'GI_UINTEGER',
+SQL_ORDER_BY_COLUMNS_IN_SELECT : 'GI_YESNO',SQL_PARAM_ARRAY_ROW_COUNTS : 'GI_UINTEGER',
+SQL_PARAM_ARRAY_SELECTS : 'GI_UINTEGER',SQL_PROCEDURES : 'GI_YESNO',SQL_PROCEDURE_TERM : 'GI_STRING',
+SQL_QUOTED_IDENTIFIER_CASE : 'GI_USMALLINT',SQL_ROW_UPDATES : 'GI_YESNO',SQL_SCHEMA_TERM : 'GI_STRING',
+SQL_SCHEMA_USAGE : 'GI_UINTEGER',SQL_SCROLL_OPTIONS : 'GI_UINTEGER',SQL_SEARCH_PATTERN_ESCAPE : 'GI_STRING',
+SQL_SERVER_NAME : 'GI_STRING',SQL_SPECIAL_CHARACTERS : 'GI_STRING',SQL_SQL92_DATETIME_FUNCTIONS : 'GI_UINTEGER',
+SQL_SQL92_FOREIGN_KEY_DELETE_RULE : 'GI_UINTEGER',SQL_SQL92_FOREIGN_KEY_UPDATE_RULE : 'GI_UINTEGER',
+SQL_SQL92_GRANT : 'GI_UINTEGER',SQL_SQL92_NUMERIC_VALUE_FUNCTIONS : 'GI_UINTEGER',
+SQL_SQL92_PREDICATES : 'GI_UINTEGER',SQL_SQL92_RELATIONAL_JOIN_OPERATORS : 'GI_UINTEGER',
+SQL_SQL92_REVOKE : 'GI_UINTEGER',SQL_SQL92_ROW_VALUE_CONSTRUCTOR : 'GI_UINTEGER',
+SQL_SQL92_STRING_FUNCTIONS : 'GI_UINTEGER',SQL_SQL92_VALUE_EXPRESSIONS : 'GI_UINTEGER',
+SQL_SQL_CONFORMANCE : 'GI_UINTEGER',SQL_STANDARD_CLI_CONFORMANCE : 'GI_UINTEGER',
+SQL_STATIC_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_STATIC_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',
+SQL_STRING_FUNCTIONS : 'GI_UINTEGER',SQL_SUBQUERIES : 'GI_UINTEGER',
+SQL_SYSTEM_FUNCTIONS : 'GI_UINTEGER',SQL_TABLE_TERM : 'GI_STRING',SQL_TIMEDATE_ADD_INTERVALS : 'GI_UINTEGER',
+SQL_TIMEDATE_DIFF_INTERVALS : 'GI_UINTEGER',SQL_TIMEDATE_FUNCTIONS : 'GI_UINTEGER',
+SQL_TXN_CAPABLE : 'GI_USMALLINT',SQL_TXN_ISOLATION_OPTION : 'GI_UINTEGER',
+SQL_UNION : 'GI_UINTEGER',SQL_USER_NAME : 'GI_STRING',SQL_XOPEN_CLI_YEAR : 'GI_STRING',
+}
+
+#Definations for types
+BINARY = bytearray
+Binary = bytearray
+DATETIME = datetime.datetime
+Date = datetime.date
+Time = datetime.time
+Timestamp = datetime.datetime
+STRING = str
+NUMBER = float
+ROWID = int
+DateFromTicks = datetime.date.fromtimestamp
+TimeFromTicks = lambda x: datetime.datetime.fromtimestamp(x).time()
+TimestampFromTicks = datetime.datetime.fromtimestamp
+
+
+#Define exceptions
+class OdbcNoLibrary(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+class OdbcLibraryError(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+class OdbcInvalidHandle(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+class OdbcGenericError(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+
+class Warning(StandardError):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+
+class Error(StandardError):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+class InterfaceError(Error):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+
+class DatabaseError(Error):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+
+class InternalError(DatabaseError):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+
+class ProgrammingError(DatabaseError):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+class DataError(DatabaseError):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+class IntegrityError(DatabaseError):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+class NotSupportedError(Error):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+class OperationalError(DatabaseError):
+ def __init__(self, error_code, error_desc):
+ self.value = (error_code, error_desc)
+ self.args = (error_code, error_desc)
+
+
+
+# Get the References of the platform's ODBC functions via ctypes
+if sys.platform in ('win32','cli'):
+ ODBC_API = ctypes.windll.odbc32
+ # On Windows, the size of SQLWCHAR is hardcoded to 2-bytes.
+ SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_ushort)
+else:
+ # Set the library location on linux
+ lib_paths = ("/usr/lib/libodbc.so","/usr/lib/i386-linux-gnu/libodbc.so","/usr/lib/x86_64-linux-gnu/libodbc.so")
+ lib_paths = [path for path in lib_paths if os.path.exists(path)]
+ if len(lib_paths) == 0 :
+ raise OdbcNoLibrary, 'ODBC Library is not found'
+ library = lib_paths[0]
+ try:
+ ODBC_API = ctypes.cdll.LoadLibrary(library)
+ except:
+ raise OdbcLibraryError, 'Error while loading %s' % library
+
+ # unixODBC defaults to 2-bytes SQLWCHAR, unless "-DSQL_WCHART_CONVERT" was
+ # added to CFLAGS, in which case it will be the size of wchar_t.
+ # Note that using 4-bytes SQLWCHAR will break most ODBC drivers, as driver
+ # development mostly targets the Windows platform.
+ import commands
+ status, output = commands.getstatusoutput('odbc_config --cflags')
+ if status == 0 and 'SQL_WCHART_CONVERT' in output:
+ SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_wchar)
+ else:
+ SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_ushort)
+
+
+create_buffer_u = ctypes.create_unicode_buffer
+create_buffer = ctypes.create_string_buffer
+wchar_type = ctypes.c_wchar_p
+to_unicode = lambda s: s
+from_buffer_u = lambda buffer: buffer.value
+
+# This is the common case on Linux, which uses wide Python build together with
+# the default unixODBC without the "-DSQL_WCHART_CONVERT" CFLAGS.
+if UNICODE_SIZE > SQLWCHAR_SIZE:
+ # We can only use unicode buffer if the size of wchar_t (UNICODE_SIZE) is
+ # the same as the size expected by the driver manager (SQLWCHAR_SIZE).
+ create_buffer_u = create_buffer
+ wchar_type = ctypes.c_char_p
+
+ def to_unicode(s):
+ return s.encode('UTF-16LE')
+
+ def from_buffer_u(buffer):
+ i = 0
+ uchars = []
+ while True:
+ uchar = buffer.raw[i:i + 2].decode('UTF-16')
+ if uchar == u'\x00':
+ break
+ uchars.append(uchar)
+ i += 2
+ return ''.join(uchars)
+
+# Exoteric case, don't really care.
+elif UNICODE_SIZE < SQLWCHAR_SIZE:
+ raise OdbcLibraryError('Using narrow Python build with ODBC library '
+ 'expecting wide unicode is not supported.')
+
+
+# Below Datatype mappings referenced the document at
+# http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.sdk_12.5.1.aseodbc/html/aseodbc/CACFDIGH.htm
+
+
+SQL_data_type_dict = { \
+#SQL Data TYPE 0.Python Data Type 1.Default Output Converter 2.Buffer Type 3.Buffer Allocator 4.Default Buffer Size
+SQL_TYPE_NULL : (None, lambda x: None, SQL_C_CHAR, create_buffer, 2 ),
+SQL_CHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ),
+SQL_NUMERIC : (Decimal, Decimal, SQL_C_CHAR, create_buffer, 150 ),
+SQL_DECIMAL : (Decimal, Decimal, SQL_C_CHAR, create_buffer, 150 ),
+SQL_INTEGER : (int, int, SQL_C_CHAR, create_buffer, 150 ),
+SQL_SMALLINT : (int, int, SQL_C_CHAR, create_buffer, 150 ),
+SQL_FLOAT : (float, float, SQL_C_CHAR, create_buffer, 150 ),
+SQL_REAL : (float, float, SQL_C_CHAR, create_buffer, 150 ),
+SQL_DOUBLE : (float, float, SQL_C_CHAR, create_buffer, 200 ),
+SQL_DATE : (datetime.date, dt_cvt, SQL_C_CHAR , create_buffer, 30 ),
+SQL_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ),
+SQL_SS_TIME2 : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ),
+SQL_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ),
+SQL_VARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ),
+SQL_LONGVARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 20500 ),
+SQL_BINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 5120 ),
+SQL_VARBINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 5120 ),
+SQL_LONGVARBINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 20500 ),
+SQL_BIGINT : (long, long, SQL_C_CHAR, create_buffer, 150 ),
+SQL_TINYINT : (int, int, SQL_C_CHAR, create_buffer, 150 ),
+SQL_BIT : (bool, lambda x:x=='1', SQL_C_CHAR, create_buffer, 2 ),
+SQL_WCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ),
+SQL_WVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ),
+SQL_GUID : (str, str, SQL_C_CHAR, create_buffer, 50 ),
+SQL_WLONGVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 20500 ),
+SQL_TYPE_DATE : (datetime.date, dt_cvt, SQL_C_CHAR, create_buffer, 30 ),
+SQL_TYPE_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ),
+SQL_TYPE_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ),
+}
+
+
+"""
+Types mapping, applicable for 32-bit and 64-bit Linux / Windows / Mac OS X.
+
+SQLPointer -> ctypes.c_void_p
+SQLCHAR * -> ctypes.c_char_p
+SQLWCHAR * -> ctypes.c_wchar_p on Windows, ctypes.c_char_p with unixODBC
+SQLINT -> ctypes.c_int
+SQLSMALLINT -> ctypes.c_short
+SQMUSMALLINT -> ctypes.c_ushort
+SQLLEN -> ctypes.c_ssize_t
+SQLULEN -> ctypes.c_size_t
+SQLRETURN -> ctypes.c_short
+"""
+
+# Define the python return type for ODBC functions with ret result.
+funcs_with_ret = [
+ "SQLAllocHandle",
+ "SQLBindParameter",
+ "SQLCloseCursor",
+ "SQLColAttribute",
+ "SQLColumns",
+ "SQLColumnsW",
+ "SQLConnect",
+ "SQLConnectW",
+ "SQLDataSources",
+ "SQLDataSourcesW",
+ "SQLDescribeCol",
+ "SQLDescribeColW",
+ "SQLDescribeParam",
+ "SQLDisconnect",
+ "SQLDriverConnect",
+ "SQLDriverConnectW",
+ "SQLEndTran",
+ "SQLExecDirect",
+ "SQLExecDirectW",
+ "SQLExecute",
+ "SQLFetch",
+ "SQLFetchScroll",
+ "SQLForeignKeys",
+ "SQLForeignKeysW",
+ "SQLFreeHandle",
+ "SQLFreeStmt",
+ "SQLGetData",
+ "SQLGetDiagRec",
+ "SQLGetInfo",
+ "SQLGetTypeInfo",
+ "SQLMoreResults",
+ "SQLNumParams",
+ "SQLNumResultCols",
+ "SQLPrepare",
+ "SQLPrepareW",
+ "SQLPrimaryKeys",
+ "SQLPrimaryKeysW",
+ "SQLProcedureColumns",
+ "SQLProcedureColumnsW",
+ "SQLProcedures",
+ "SQLProceduresW",
+ "SQLRowCount",
+ "SQLSetConnectAttr",
+ "SQLSetEnvAttr",
+ "SQLStatistics",
+ "SQLStatisticsW",
+ "SQLTables",
+ "SQLTablesW",
+]
+
+for func_name in funcs_with_ret:
+ getattr(ODBC_API, func_name).restype = ctypes.c_short
+
+if sys.platform not in ('cli'):
+ #Seems like the IronPython can not declare ctypes.POINTER type arguments
+ ODBC_API.SQLAllocHandle.argtypes = [
+ ctypes.c_short,
+ ctypes.c_void_p,
+ ctypes.POINTER(ctypes.c_void_p),
+ ]
+
+ ODBC_API.SQLBindParameter.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_ushort,
+ ctypes.c_short,
+ ctypes.c_short,
+ ctypes.c_short,
+ ctypes.c_size_t,
+ ctypes.c_short,
+ ctypes.c_void_p,
+ ctypes.c_ssize_t,
+ ctypes.POINTER(ctypes.c_ssize_t),
+ ]
+
+ ODBC_API.SQLColAttribute.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_ushort,
+ ctypes.c_ushort,
+ ctypes.c_void_p,
+ ctypes.c_short,
+ ctypes.POINTER(ctypes.c_short),
+ ctypes.POINTER(ctypes.c_ssize_t),
+ ]
+
+ ODBC_API.SQLDataSources.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_ushort,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.POINTER(ctypes.c_short),
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.POINTER(ctypes.c_short),
+ ]
+
+ ODBC_API.SQLDescribeCol.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_ushort,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.POINTER(ctypes.c_short),
+ ctypes.POINTER(ctypes.c_short),
+ ctypes.POINTER(ctypes.c_size_t),
+ ctypes.POINTER(ctypes.c_short),
+ ctypes.POINTER(ctypes.c_short),
+ ]
+
+ ODBC_API.SQLDescribeParam.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_ushort,
+ ctypes.POINTER(ctypes.c_short),
+ ctypes.POINTER(ctypes.c_size_t),
+ ctypes.POINTER(ctypes.c_short),
+ ctypes.POINTER(ctypes.c_short),
+ ]
+
+ ODBC_API.SQLDriverConnect.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.POINTER(ctypes.c_short),
+ ctypes.c_ushort,
+ ]
+
+ ODBC_API.SQLGetData.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_ushort,
+ ctypes.c_short,
+ ctypes.c_void_p,
+ ctypes.c_ssize_t,
+ ctypes.POINTER(ctypes.c_ssize_t),
+ ]
+
+ ODBC_API.SQLGetDiagRec.argtypes = [
+ ctypes.c_short,
+ ctypes.c_void_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.POINTER(ctypes.c_int),
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.POINTER(ctypes.c_short),
+ ]
+
+ ODBC_API.SQLGetInfo.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_ushort,
+ ctypes.c_void_p,
+ ctypes.c_short,
+ ctypes.POINTER(ctypes.c_short),
+ ]
+
+ ODBC_API.SQLRowCount.argtypes = [
+ ctypes.c_void_p,
+ ctypes.POINTER(ctypes.c_ssize_t),
+ ]
+
+ ODBC_API.SQLNumParams.argtypes = [
+ ctypes.c_void_p,
+ ctypes.POINTER(ctypes.c_short),
+ ]
+
+ ODBC_API.SQLNumResultCols.argtypes = [
+ ctypes.c_void_p,
+ ctypes.POINTER(ctypes.c_short),
+ ]
+
+
+ODBC_API.SQLCloseCursor.argtypes = [ctypes.c_void_p]
+
+ODBC_API.SQLColumns.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+]
+
+ODBC_API.SQLConnect.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+]
+
+
+
+ODBC_API.SQLDisconnect.argtypes = [ctypes.c_void_p]
+
+
+ODBC_API.SQLEndTran.argtypes = [
+ ctypes.c_short,
+ ctypes.c_void_p,
+ ctypes.c_short,
+]
+
+ODBC_API.SQLExecute.argtypes = [ctypes.c_void_p]
+
+ODBC_API.SQLExecDirect.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_int,
+]
+
+ODBC_API.SQLFetch.argtypes = [ctypes.c_void_p]
+
+ODBC_API.SQLFetchScroll.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_short,
+ ctypes.c_ssize_t,
+]
+
+ODBC_API.SQLForeignKeys.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+]
+
+ODBC_API.SQLFreeHandle.argtypes = [
+ ctypes.c_short,
+ ctypes.c_void_p,
+]
+
+ODBC_API.SQLFreeStmt.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_ushort,
+]
+
+
+ODBC_API.SQLGetTypeInfo.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_short,
+]
+
+ODBC_API.SQLMoreResults.argtypes = [ctypes.c_void_p]
+
+
+ODBC_API.SQLPrepare.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_int,
+]
+
+ODBC_API.SQLPrimaryKeys.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+]
+
+ODBC_API.SQLProcedureColumns.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+]
+
+ODBC_API.SQLProcedures.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+]
+
+
+ODBC_API.SQLSetConnectAttr.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_int,
+]
+
+ODBC_API.SQLSetEnvAttr.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_int,
+]
+
+ODBC_API.SQLStatistics.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_ushort,
+ ctypes.c_ushort,
+]
+
+ODBC_API.SQLTables.argtypes = [
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+ ctypes.c_char_p,
+ ctypes.c_short,
+]
+
+def to_wchar(argtypes):
+ if argtypes: # Under IronPython some argtypes are not declared
+ result = []
+ for x in argtypes:
+ if x == ctypes.c_char_p:
+ result.append(wchar_type)
+ else:
+ result.append(x)
+ return result
+ else:
+ return argtypes
+
+ODBC_API.SQLColumnsW.argtypes = to_wchar(ODBC_API.SQLColumns.argtypes)
+ODBC_API.SQLConnectW.argtypes = to_wchar(ODBC_API.SQLConnect.argtypes)
+ODBC_API.SQLDataSourcesW.argtypes = to_wchar(ODBC_API.SQLDataSources.argtypes)
+ODBC_API.SQLDescribeColW.argtypes = to_wchar(ODBC_API.SQLDescribeCol.argtypes)
+ODBC_API.SQLDriverConnectW.argtypes = to_wchar(ODBC_API.SQLDriverConnect.argtypes)
+ODBC_API.SQLExecDirectW.argtypes = to_wchar(ODBC_API.SQLExecDirect.argtypes)
+ODBC_API.SQLForeignKeysW.argtypes = to_wchar(ODBC_API.SQLForeignKeys.argtypes)
+ODBC_API.SQLPrepareW.argtypes = to_wchar(ODBC_API.SQLPrepare.argtypes)
+ODBC_API.SQLPrimaryKeysW.argtypes = to_wchar(ODBC_API.SQLPrimaryKeys.argtypes)
+ODBC_API.SQLProcedureColumnsW.argtypes = to_wchar(ODBC_API.SQLProcedureColumns.argtypes)
+ODBC_API.SQLProceduresW.argtypes = to_wchar(ODBC_API.SQLProcedures.argtypes)
+ODBC_API.SQLStatisticsW.argtypes = to_wchar(ODBC_API.SQLStatistics.argtypes)
+ODBC_API.SQLTablesW.argtypes = to_wchar(ODBC_API.SQLTables.argtypes)
+
+
+# Set the alias for the ctypes functions for beter code readbility or performance.
+ADDR = ctypes.byref
+SQLFetch = ODBC_API.SQLFetch
+SQLExecute = ODBC_API.SQLExecute
+SQLBindParameter = ODBC_API.SQLBindParameter
+
+
+
+
+
+def ctrl_err(ht, h, val_ret):
+ """Classify type of ODBC error from (type of handle, handle, return value)
+ , and raise with a list"""
+ state = create_buffer(5)
+ NativeError = ctypes.c_int()
+ Message = create_buffer(1024*10)
+ Buffer_len = ctypes.c_short()
+ err_list = []
+ number_errors = 1
+
+ while 1:
+ ret = ODBC_API.SQLGetDiagRec(ht, h, number_errors, state, \
+ NativeError, Message, len(Message), ADDR(Buffer_len))
+ if ret == SQL_NO_DATA_FOUND:
+ #No more data, I can raise
+ if DEBUG: print err_list[0][1]
+ state = err_list[0][0]
+ err_text = '['+state+'] '+err_list[0][1]
+ if state[:2] in ('24','25','42'):
+ raise ProgrammingError(state,err_text)
+ elif state[:2] in ('22'):
+ raise DataError(state,err_text)
+ elif state[:2] in ('23') or state == '40002':
+ raise IntegrityError(state,err_text)
+ elif state == '0A000':
+ raise NotSupportedError(state,err_text)
+ elif state in ('HYT00','HYT01'):
+ raise OperationalError(state,err_text)
+ elif state[:2] in ('IM','HY'):
+ raise Error(state,err_text)
+ else:
+ raise DatabaseError(state,err_text)
+ break
+ elif ret == SQL_INVALID_HANDLE:
+ #The handle passed is an invalid handle
+ raise ProgrammingError('', 'SQL_INVALID_HANDLE')
+ elif ret == SQL_SUCCESS:
+ err_list.append((state.value, Message.value, NativeError.value))
+ number_errors += 1
+
+
+def validate(ret, handle_type, handle):
+ """ Validate return value, if not success, raise exceptions based on the handle """
+ if ret not in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA):
+ ctrl_err(handle_type, handle, ret)
+
+
+def AllocateEnv():
+ if pooling:
+ ret = ODBC_API.SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, SQL_CP_ONE_PER_HENV, SQL_IS_UINTEGER)
+ validate(ret, SQL_HANDLE_ENV, SQL_NULL_HANDLE)
+
+ '''
+ Allocate an ODBC environment by initializing the handle shared_env_h
+ ODBC enviroment needed to be created, so connections can be created under it
+ connections pooling can be shared under one environment
+ '''
+ global shared_env_h
+ shared_env_h = ctypes.c_void_p()
+ ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, ADDR(shared_env_h))
+ validate(ret, SQL_HANDLE_ENV, shared_env_h)
+
+ # Set the ODBC environment's compatibil leve to ODBC 3.0
+ ret = ODBC_API.SQLSetEnvAttr(shared_env_h, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0)
+ validate(ret, SQL_HANDLE_ENV, shared_env_h)
+
+
+"""
+Here, we have a few callables that determine how a result row is returned.
+
+A new one can be added by creating a callable that:
+- accepts a cursor as its parameter.
+- returns a callable that accepts an iterable containing the row values.
+"""
+
+def TupleRow(cursor):
+ """Normal tuple with added attribute `cursor_description`, as in pyodbc.
+
+ This is the default.
+ """
+ class Row(tuple):
+ cursor_description = cursor.description
+
+ return Row
+
+
+def NamedTupleRow(cursor):
+ """Named tuple to allow attribute lookup by name.
+
+ Requires py2.6 or above.
+ """
+ from collections import namedtuple
+
+ attr_names = [x[0] for x in cursor._ColBufferList]
+
+ class Row(namedtuple('Row', attr_names, rename=True)):
+ cursor_description = cursor.description
+
+ def __new__(cls, iterable):
+ return super(Row, cls).__new__(cls, *iterable)
+
+ return Row
+
+
+def MutableNamedTupleRow(cursor):
+ """Mutable named tuple to allow attribute to be replaced. This should be
+ compatible with pyodbc's Row type.
+
+ Requires 3rd-party library "recordtype".
+ """
+ from recordtype import recordtype
+
+ attr_names = [x[0] for x in cursor._ColBufferList]
+
+ class Row(recordtype('Row', attr_names, rename=True)):
+ cursor_description = cursor.description
+
+ def __init__(self, iterable):
+ super(Row, self).__init__(*iterable)
+
+ def __iter__(self):
+ for field_name in self.__slots__:
+ yield getattr(self, field_name)
+
+ def __getitem__(self, index):
+ if isinstance(index, slice):
+ return tuple(getattr(self, x) for x in self.__slots__[index])
+ return getattr(self, self.__slots__[index])
+
+ def __setitem__(self, index, value):
+ setattr(self, self.__slots__[index], value)
+
+ return Row
+
+
+# The get_type function is used to determine if parameters need to be re-binded
+# against the changed parameter types
+def get_type(v):
+ t = type(v)
+ if t == str:
+ if len(v) >= 255:
+ t = 's'
+ if t == unicode:
+ if len(v) >= 255:
+ t = 'u'
+ if t == Decimal:
+ sv = str(v).replace('-','').strip('0').split('.')
+ if len(sv)>1:
+ t = (len(sv[0])+len(sv[1]),len(sv[1]))
+ else:
+ t = (len(sv[0]),0)
+ return t
+
+
+
+# The Cursor Class.
+class Cursor:
+ def __init__(self, conx, row_type_callable=None):
+ """ Initialize self._stmt_h, which is the handle of a statement
+ A statement is actually the basis of a python"cursor" object
+ """
+ self._stmt_h = ctypes.c_void_p()
+ self.connection = conx
+ self.row_type_callable = row_type_callable or TupleRow
+ self.statement = None
+ self._last_param_types = None
+ self._ParamBufferList = []
+ self._ColBufferList = []
+ self._row_type = None
+ self._buf_cvt_func = []
+ self.rowcount = -1
+ self.description = None
+ self.autocommit = None
+ self._ColTypeCodeList = []
+ self._outputsize = {}
+ self._inputsizers = []
+ self.arraysize = 1
+ ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_STMT, self.connection.dbc_h, ADDR(self._stmt_h))
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+ self.closed = False
+
+
+ def execute(self, query_string, params=None, many_mode=False, call_mode=False):
+ """ Execute the query string, with optional parameters.
+ If parameters are provided, the query would first be prepared, then executed with parameters;
+ If parameters are not provided, only th query sting, it would be executed directly
+ """
+
+ self._free_results('FREE_STATEMENT')
+
+ if params:
+ # If parameters exist, first prepare the query then executed with parameters
+ if not type(params) in (tuple, list, set):
+ raise TypeError("Params must be in a list, tuple, or set")
+
+ if not many_mode:
+ if query_string != self.statement:
+ # if the query is not same as last query, then it is not prepared
+ self.prepare(query_string)
+
+
+ param_types = map(get_type, params)
+
+ if call_mode:
+ self._BindParams(param_types, self._pram_io_list)
+ else:
+ if param_types != self._last_param_types:
+ self._BindParams(param_types)
+
+
+ # With query prepared, now put parameters into buffers
+ col_num = 0
+ for param_buffer, param_buffer_len, sql_type in self._ParamBufferList:
+ c_char_buf, c_buf_len = '', 0
+ param_val = params[col_num]
+ if param_val is None:
+ c_buf_len = SQL_NULL_DATA
+
+ elif type(param_val) == datetime.datetime:
+ max_len = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0]
+ datetime_str = param_val.strftime('%Y-%m-%d %H:%M:%S.%f')
+ c_char_buf = datetime_str[:max_len]
+ c_buf_len = len(c_char_buf)
+ # print c_buf_len, c_char_buf
+
+ elif type(param_val) == datetime.date:
+ if self.connection.type_size_dic.has_key(SQL_TYPE_DATE):
+ max_len = self.connection.type_size_dic[SQL_TYPE_DATE][0]
+ else:
+ max_len = 10
+ c_char_buf = param_val.isoformat()[:max_len]
+ c_buf_len = len(c_char_buf)
+ #print c_char_buf
+
+ elif type(param_val) == datetime.time:
+ if self.connection.type_size_dic.has_key(SQL_TYPE_TIME):
+ max_len = self.connection.type_size_dic[SQL_TYPE_TIME][0]
+ c_char_buf = param_val.isoformat()[:max_len]
+ c_buf_len = len(c_char_buf)
+ elif self.connection.type_size_dic.has_key(SQL_SS_TIME2):
+ max_len = self.connection.type_size_dic[SQL_SS_TIME2][0]
+ c_char_buf = param_val.isoformat()[:max_len]
+ c_buf_len = len(c_char_buf)
+ else:
+ c_buf_len = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0]
+ time_str = param_val.isoformat()
+ if len(time_str) == 8:
+ time_str += '.000'
+ c_char_buf = '1900-01-01 '+time_str[0:c_buf_len - 11]
+ #print c_buf_len, c_char_buf
+
+ elif type(param_val) == bool:
+ if param_val == True:
+ c_char_buf = '1'
+ else:
+ c_char_buf = '0'
+ c_buf_len = 1
+
+ elif type(param_val) in (int, long, float, Decimal):
+ c_char_buf = str(param_val)
+ c_buf_len = len(c_char_buf)
+
+ elif type(param_val) in (str,):
+ c_char_buf = param_val
+ c_buf_len = len(c_char_buf)
+ elif type(param_val) in (unicode,):
+ c_char_buf = to_unicode(param_val)
+ c_buf_len = len(c_char_buf)
+ elif type(param_val) in (bytearray,buffer):
+ c_char_buf = str(param_val)
+ c_buf_len = len(c_char_buf)
+
+ else:
+ c_char_buf = param_val
+
+
+ if type(param_val) in (bytearray,buffer):
+ param_buffer.raw = c_char_buf
+
+ else:
+ param_buffer.value = c_char_buf
+ #print param_buffer, param_buffer.value
+
+ if type(param_val) in (unicode,str,'u','s'):
+ #ODBC driver will find NUL in unicode and string to determine their length
+ param_buffer_len.value = SQL_NTS
+ else:
+ param_buffer_len.value = c_buf_len
+
+ col_num += 1
+ ret = SQLExecute(self._stmt_h)
+ if ret != SQL_SUCCESS:
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+
+ if not many_mode:
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+
+ else:
+ self.execdirect(query_string)
+ return (self)
+
+
+ def _SQLExecute(self):
+ ret = SQLExecute(self._stmt_h)
+ if ret != SQL_SUCCESS:
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+
+
+
+ def prepare(self, query_string):
+ """prepare a query"""
+ if type(query_string) == unicode:
+ c_query_string = wchar_type(to_unicode(query_string))
+ ret = ODBC_API.SQLPrepareW(self._stmt_h, c_query_string, len(query_string))
+ else:
+ c_query_string = ctypes.c_char_p(query_string)
+ ret = ODBC_API.SQLPrepare(self._stmt_h, c_query_string, len(query_string))
+ if ret != SQL_SUCCESS:
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+ self.statement = query_string
+
+
+ def execdirect(self, query_string):
+ """Execute a query directly"""
+ if type(query_string) == unicode:
+ c_query_string = wchar_type(to_unicode(query_string))
+ ret = ODBC_API.SQLExecDirectW(self._stmt_h, c_query_string, len(query_string))
+ else:
+ c_query_string = ctypes.c_char_p(query_string)
+ ret = ODBC_API.SQLExecDirect(self._stmt_h, c_query_string, len(query_string))
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+ self.statement = None
+ return (self)
+
+
+ def callproc(self, procname, args):
+ raise Warning('', 'Still not fully implemented')
+ self._pram_io_list = [row[4] for row in self.procedurecolumns(procedure = procname).fetchall() if row[4] not in (SQL_RESULT_COL, SQL_RETURN_VALUE)]
+
+ print 'pram_io_list: '+str(self._pram_io_list)
+
+
+
+ call_escape = '{CALL '+procname
+ if args:
+ call_escape += '(' + ','.join(['?' for params in args]) + ')'
+ call_escape += '}'
+
+ self.execute(call_escape, args, call_mode = True)
+
+ result = []
+
+ for buf, buf_len, sql_type in self._ParamBufferList:
+ if buf_len.value == -1:
+ result.append(None)
+ else:
+ result.append(self.connection.output_converter[sql_type](buf.value))
+ return (result)
+
+
+
+ def executemany(self, query_string, params_list = [None]):
+ self.prepare(query_string)
+ for params in params_list:
+ self.execute(query_string, params, many_mode = True)
+ self._NumOfRows()
+ self.rowcount = -1
+ self._UpdateDesc()
+ #self._BindCols()
+
+
+ def _BindParams(self, param_types, pram_io_list = []):
+ """Create parameter buffers based on param types, and bind them to the statement"""
+ # Get the number of query parameters judged by database.
+ NumParams = ctypes.c_short()
+ ret = ODBC_API.SQLNumParams(self._stmt_h, ADDR(NumParams))
+ if ret != SQL_SUCCESS:
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ if len(param_types) != NumParams.value:
+ # In case number of parameters provided do not same as number required
+ error_desc = "The SQL contains %d parameter markers, but %d parameters were supplied" \
+ %(NumParams.value,len(param_types))
+ raise ProgrammingError('HY000',error_desc)
+
+
+ # Every parameter needs to be binded to a buffer
+ ParamBufferList = []
+ # Temporary holder since we can only call SQLDescribeParam before
+ # calling SQLBindParam.
+ temp_holder = []
+ for col_num in range(NumParams.value):
+ col_size = 0
+ buf_size = 512
+
+ if param_types[col_num] == type(None):
+ ParameterNumber = ctypes.c_ushort(col_num + 1)
+ DataType = ctypes.c_short()
+ ParameterSize = ctypes.c_size_t()
+ DecimalDigits = ctypes.c_short()
+ Nullable = ctypes.c_short()
+ ret = ODBC_API.SQLDescribeParam(
+ self._stmt_h,
+ ParameterNumber,
+ ADDR(DataType),
+ ADDR(ParameterSize),
+ ADDR(DecimalDigits),
+ ADDR(Nullable),
+ )
+ if ret != SQL_SUCCESS:
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ sql_c_type = SQL_C_DEFAULT
+ sql_type = DataType.value
+ buf_size = 1
+ ParameterBuffer = create_buffer(buf_size)
+
+ elif param_types[col_num] in (int,):
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_INTEGER
+ buf_size = SQL_data_type_dict[sql_type][4]
+ ParameterBuffer = create_buffer(buf_size)
+
+ elif param_types[col_num] in (long,):
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_BIGINT
+ buf_size = SQL_data_type_dict[sql_type][4]
+ ParameterBuffer = create_buffer(buf_size)
+
+
+ elif param_types[col_num] == float:
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_DOUBLE
+ buf_size = SQL_data_type_dict[sql_type][4]
+ ParameterBuffer = create_buffer(buf_size)
+
+ elif param_types[col_num] == bool:
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_BIT
+ buf_size = SQL_data_type_dict[sql_type][4]
+ ParameterBuffer = create_buffer(buf_size)
+
+
+ elif type(param_types[col_num]) == tuple: #Decimal
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_NUMERIC
+ buf_size = param_types[col_num][0]
+
+ ParameterBuffer = create_buffer(buf_size+4)
+ col_size = param_types[col_num][1]
+ if DEBUG: print param_types[col_num][0],param_types[col_num][1]
+
+ elif param_types[col_num] == datetime.datetime:
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_TYPE_TIMESTAMP
+ buf_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0]
+ ParameterBuffer = create_buffer(buf_size)
+ col_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][1]
+
+
+ elif param_types[col_num] == datetime.date:
+ sql_c_type = SQL_C_CHAR
+ if self.connection.type_size_dic.has_key(SQL_TYPE_DATE):
+ if DEBUG: print 'conx.type_size_dic.has_key(SQL_TYPE_DATE)'
+ sql_type = SQL_TYPE_DATE
+ buf_size = self.connection.type_size_dic[SQL_TYPE_DATE][0]
+
+ ParameterBuffer = create_buffer(buf_size)
+ col_size = self.connection.type_size_dic[SQL_TYPE_DATE][1]
+
+ else:
+ # SQL Sever <2008 doesn't have a DATE type.
+ sql_type = SQL_TYPE_TIMESTAMP
+ buf_size = 10
+ ParameterBuffer = create_buffer(buf_size)
+
+
+ elif param_types[col_num] == datetime.time:
+ sql_c_type = SQL_C_CHAR
+ if self.connection.type_size_dic.has_key(SQL_TYPE_TIME):
+ sql_type = SQL_TYPE_TIME
+ buf_size = self.connection.type_size_dic[SQL_TYPE_TIME][0]
+ ParameterBuffer = create_buffer(buf_size)
+ col_size = self.connection.type_size_dic[SQL_TYPE_TIME][1]
+ elif self.connection.type_size_dic.has_key(SQL_SS_TIME2):
+ # TIME type added in SQL Server 2008
+ sql_type = SQL_SS_TIME2
+ buf_size = self.connection.type_size_dic[SQL_SS_TIME2][0]
+ ParameterBuffer = create_buffer(buf_size)
+ col_size = self.connection.type_size_dic[SQL_SS_TIME2][1]
+ else:
+ # SQL Sever <2008 doesn't have a TIME type.
+ sql_type = SQL_TYPE_TIMESTAMP
+ buf_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0]
+ ParameterBuffer = create_buffer(buf_size)
+ col_size = 3
+
+ elif param_types[col_num] == unicode:
+ sql_c_type = SQL_C_WCHAR
+ sql_type = SQL_WVARCHAR
+ buf_size = 255
+ ParameterBuffer = create_buffer_u(buf_size)
+
+ elif param_types[col_num] == str:
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_VARCHAR
+ buf_size = 255
+ ParameterBuffer = create_buffer(buf_size)
+
+ elif param_types[col_num] == 'u':
+ sql_c_type = SQL_C_WCHAR
+ sql_type = SQL_WLONGVARCHAR
+ buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500
+ ParameterBuffer = create_buffer_u(buf_size)
+
+ elif param_types[col_num] == 's':
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_LONGVARCHAR
+ buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500
+ ParameterBuffer = create_buffer(buf_size)
+
+
+ elif param_types[col_num] in (bytearray, buffer):
+ sql_c_type = SQL_C_BINARY
+ sql_type = SQL_LONGVARBINARY
+ buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500
+ ParameterBuffer = create_buffer(buf_size)
+
+
+ else:
+ sql_c_type = SQL_C_CHAR
+ sql_type = SQL_LONGVARCHAR
+ buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500
+ ParameterBuffer = create_buffer(buf_size)
+
+ temp_holder.append((sql_c_type, sql_type, buf_size, col_size, ParameterBuffer))
+
+ for col_num, (sql_c_type, sql_type, buf_size, col_size, ParameterBuffer) in enumerate(temp_holder):
+ BufferLen = ctypes.c_ssize_t(buf_size)
+ LenOrIndBuf = ctypes.c_ssize_t()
+
+
+ InputOutputType = SQL_PARAM_INPUT
+ if len(pram_io_list) > col_num:
+ InputOutputType = pram_io_list[col_num]
+
+ ret = SQLBindParameter(self._stmt_h, col_num + 1, InputOutputType, sql_c_type, sql_type, buf_size,\
+ col_size, ADDR(ParameterBuffer), BufferLen,ADDR(LenOrIndBuf))
+ if ret != SQL_SUCCESS:
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+ # Append the value buffer and the lenth buffer to the array
+ ParamBufferList.append((ParameterBuffer,LenOrIndBuf,sql_type))
+
+ self._last_param_types = param_types
+ self._ParamBufferList = ParamBufferList
+
+
+
+ def _CreateColBuf(self):
+ NOC = self._NumOfCols()
+ self._ColBufferList = []
+ self._row_type = None
+ for col_num in range(NOC):
+ col_name = self.description[col_num][0]
+
+ col_sql_data_type = self._ColTypeCodeList[col_num]
+
+ # set default size base on the column's sql data type
+ total_buf_len = SQL_data_type_dict[col_sql_data_type][4]
+ # over-write if there's preset size value for "large columns"
+ if total_buf_len >= 20500:
+ total_buf_len = self._outputsize.get(None,total_buf_len)
+ # over-write if there's preset size value for the "col_num" column
+ total_buf_len = self._outputsize.get(col_num, total_buf_len)
+
+
+ alloc_buffer = SQL_data_type_dict[col_sql_data_type][3](total_buf_len)
+
+ used_buf_len = ctypes.c_ssize_t()
+
+ target_type = SQL_data_type_dict[col_sql_data_type][2]
+ force_unicode = self.connection.unicode_results
+
+ if force_unicode and col_sql_data_type in (SQL_CHAR,SQL_VARCHAR,SQL_LONGVARCHAR):
+ target_type = SQL_C_WCHAR
+ alloc_buffer = create_buffer_u(total_buf_len)
+
+ buf_cvt_func = self.connection.output_converter[self._ColTypeCodeList[col_num]]
+
+ self._ColBufferList.append([col_name, target_type, used_buf_len, alloc_buffer, total_buf_len, buf_cvt_func])
+
+
+ def _GetData(self):
+ '''Bind buffers for the record set columns'''
+
+ # Lazily create the row type on first fetch.
+ if self._row_type is None:
+ self._row_type = self.row_type_callable(self)
+
+ value_list = []
+ col_num = 0
+ for col_name, target_type, used_buf_len, alloc_buffer, total_buf_len, buf_cvt_func in self._ColBufferList:
+
+ blocks = []
+ while True:
+ ret = ODBC_API.SQLGetData(self._stmt_h, col_num + 1, target_type, ADDR(alloc_buffer), total_buf_len,\
+ ADDR(used_buf_len))
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ if ret == SQL_SUCCESS:
+ if used_buf_len.value == SQL_NULL_DATA:
+ blocks.append(None)
+ else:
+ if target_type == SQL_C_BINARY:
+ blocks.append(alloc_buffer.raw[:used_buf_len.value])
+ elif target_type == SQL_C_WCHAR:
+ blocks.append(from_buffer_u(alloc_buffer))
+ else:
+ #print col_name, target_type, alloc_buffer.value
+ blocks.append(alloc_buffer.value)
+
+ break
+
+ if ret == SQL_SUCCESS_WITH_INFO:
+ if target_type == SQL_C_BINARY:
+ blocks.append(alloc_buffer.raw)
+ else:
+ blocks.append(alloc_buffer.value)
+
+ if ret == SQL_NO_DATA:
+ break
+
+
+ if len(blocks) == 1:
+ raw_value = blocks[0]
+ else:
+ raw_value = ''.join(blocks)
+
+ if raw_value == None:
+ value_list.append(None)
+ else:
+ value_list.append(buf_cvt_func(raw_value))
+ col_num += 1
+
+ return self._row_type(value_list)
+
+
+ def _UpdateDesc(self):
+ "Get the information of (name, type_code, display_size, internal_size, col_precision, scale, null_ok)"
+ Cname = create_buffer(1024)
+ Cname_ptr = ctypes.c_short()
+ Ctype_code = ctypes.c_short()
+ Csize = ctypes.c_size_t()
+ Cdisp_size = ctypes.c_ssize_t(0)
+ CDecimalDigits = ctypes.c_short()
+ Cnull_ok = ctypes.c_short()
+ ColDescr = []
+ self._ColTypeCodeList = []
+ NOC = self._NumOfCols()
+ for col in range(1, NOC+1):
+ ret = ODBC_API.SQLColAttribute(self._stmt_h, col, SQL_DESC_DISPLAY_SIZE, ADDR(create_buffer(10)),
+ 10, ADDR(ctypes.c_short()),ADDR(Cdisp_size))
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ ret = ODBC_API.SQLDescribeCol(self._stmt_h, col, Cname, len(Cname), ADDR(Cname_ptr),\
+ ADDR(Ctype_code),ADDR(Csize),ADDR(CDecimalDigits), ADDR(Cnull_ok))
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ col_name = Cname.value
+ if lowercase:
+ col_name = str.lower(col_name)
+ #(name, type_code, display_size,
+ # internal_size, col_precision, scale, null_ok)
+ ColDescr.append((col_name, SQL_data_type_dict.get(Ctype_code.value,(Ctype_code.value))[0],Cdisp_size.value,\
+ Csize.value, Csize.value,CDecimalDigits.value,Cnull_ok.value == 1 and True or False))
+ self._ColTypeCodeList.append(Ctype_code.value)
+
+ if len(ColDescr) > 0:
+ self.description = ColDescr
+ else:
+ self.description = None
+ self._CreateColBuf()
+
+
+ def _NumOfRows(self):
+ """Get the number of rows"""
+ NOR = ctypes.c_ssize_t()
+ ret = ODBC_API.SQLRowCount(self._stmt_h, ADDR(NOR))
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+ self.rowcount = NOR.value
+ return self.rowcount
+
+
+ def _NumOfCols(self):
+ """Get the number of cols"""
+ NOC = ctypes.c_short()
+ ret = ODBC_API.SQLNumResultCols(self._stmt_h, ADDR(NOC))
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+ return NOC.value
+
+
+ def fetchall(self):
+ rows = []
+ while True:
+ row = self.fetchone()
+ if row == None:
+ break
+ rows.append(row)
+ return rows
+
+
+ def fetchmany(self, num = None):
+ if num == None:
+ num = self.arraysize
+ rows, row_num = [], 0
+
+ while row_num < num:
+ row = self.fetchone()
+ if row == None:
+ break
+ rows.append(row)
+ row_num += 1
+ return rows
+
+
+ def fetchone(self):
+ ret = SQLFetch(self._stmt_h)
+ if ret == SQL_SUCCESS:
+ return self._GetData()
+ else:
+ if ret == SQL_NO_DATA_FOUND:
+ return None
+ else:
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ def next(self):
+ row = self.fetchone()
+ if row == None:
+ raise(StopIteration)
+ return row
+
+ def __iter__(self):
+ return self
+
+
+ def skip(self, count = 0):
+ for i in xrange(count):
+ ret = ODBC_API.SQLFetchScroll(self._stmt_h, SQL_FETCH_NEXT, 0)
+ if ret != SQL_SUCCESS:
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+ return None
+
+
+
+ def nextset(self):
+ ret = ODBC_API.SQLMoreResults(self._stmt_h)
+ if ret not in (SQL_SUCCESS, SQL_NO_DATA):
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ if ret == SQL_NO_DATA:
+ self._free_results('FREE_STATEMENT')
+ return False
+ else:
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+ return True
+
+
+ def _free_results(self, free_statement):
+ if not self.connection.connected:
+ raise ProgrammingError('HY000','Attempt to use a closed connection.')
+
+ self.description = None
+ if free_statement == 'FREE_STATEMENT':
+ ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_CLOSE)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+ else:
+ ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_UNBIND)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_RESET_PARAMS)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self.rowcount = -1
+
+
+
+ def getTypeInfo(self, sqlType = None):
+ if sqlType == None:
+ type = SQL_ALL_TYPES
+ else:
+ type = sqlType
+ ret = ODBC_API.SQLGetTypeInfo(self._stmt_h, type)
+ if ret in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO):
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+ return self.fetchone()
+
+
+ def tables(self, table=None, catalog=None, schema=None, tableType=None):
+ """Return a list with all tables"""
+ l_catalog = l_schema = l_table = l_tableType = 0
+
+ if catalog != None:
+ l_catalog = len(catalog)
+ catalog = ctypes.c_char_p(catalog)
+
+ if schema != None:
+ l_schema = len(schema)
+ schema = ctypes.c_char_p(schema)
+
+ if table != None:
+ l_table = len(table)
+ table = ctypes.c_char_p(table)
+
+ if tableType != None:
+ l_tableType = len(tableType)
+ tableType = ctypes.c_char_p(tableType)
+
+ self._free_results('FREE_STATEMENT')
+ self.statement = None
+ ret = ODBC_API.SQLTables(self._stmt_h,
+ catalog, l_catalog,
+ schema, l_schema,
+ table, l_table,
+ tableType, l_tableType)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+ return (self)
+
+
+ def columns(self, table=None, catalog=None, schema=None, column=None):
+ """Return a list with all columns"""
+ l_catalog = l_schema = l_table = l_column = 0
+ if catalog != None:
+ l_catalog = len(catalog)
+ catalog = ctypes.c_char_p(catalog)
+ if schema != None:
+ l_schema = len(schema)
+ schema = ctypes.c_char_p(schema)
+ if table != None:
+ l_table = len(table)
+ table = ctypes.c_char_p(table)
+ if column != None:
+ l_column = len(column)
+ column = ctypes.c_char_p(column)
+
+ self._free_results('FREE_STATEMENT')
+ self.statement = None
+
+ ret = ODBC_API.SQLColumns(self._stmt_h,
+ catalog, l_catalog,
+ schema, l_schema,
+ table, l_table,
+ column, l_column)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+ return (self)
+
+
+ def primaryKeys(self, table=None, catalog=None, schema=None):
+ l_catalog = l_schema = l_table = 0
+ if catalog != None:
+ l_catalog = len(catalog)
+ catalog = ctypes.c_char_p(catalog)
+
+ if schema != None:
+ l_schema = len(schema)
+ schema = ctypes.c_char_p(schema)
+
+ if table != None:
+ l_table = len(table)
+ table = ctypes.c_char_p(table)
+
+ self._free_results('FREE_STATEMENT')
+ self.statement = None
+
+ ret = ODBC_API.SQLPrimaryKeys(self._stmt_h,
+ catalog, l_catalog,
+ schema, l_schema,
+ table, l_table)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+ return (self)
+
+
+ def foreignKeys(self, table=None, catalog=None, schema=None, foreignTable=None, foreignCatalog=None, foreignSchema=None):
+ l_catalog = l_schema = l_table = l_foreignTable = l_foreignCatalog = l_foreignSchema = 0
+ if catalog != None:
+ l_catalog = len(catalog)
+ catalog = ctypes.c_char_p(catalog)
+ if schema != None:
+ l_schema = len(schema)
+ schema = ctypes.c_char_p(schema)
+ if table != None:
+ l_table = len(table)
+ table = ctypes.c_char_p(table)
+ if foreignTable != None:
+ l_foreignTable = len(foreignTable)
+ foreignTable = ctypes.c_char_p(foreignTable)
+ if foreignCatalog != None:
+ l_foreignCatalog = len(foreignCatalog)
+ foreignCatalog = ctypes.c_char_p(foreignCatalog)
+ if foreignSchema != None:
+ l_foreignSchema = len(foreignSchema)
+ foreignSchema = ctypes.c_char_p(foreignSchema)
+
+ self._free_results('FREE_STATEMENT')
+ self.statement = None
+
+ ret = ODBC_API.SQLForeignKeys(self._stmt_h,
+ catalog, l_catalog,
+ schema, l_schema,
+ table, l_table,
+ foreignCatalog, l_foreignCatalog,
+ foreignSchema, l_foreignSchema,
+ foreignTable, l_foreignTable)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+ return (self)
+
+
+ def procedurecolumns(self, procedure=None, catalog=None, schema=None, column=None):
+ l_catalog = l_schema = l_procedure = l_column = 0
+ if catalog != None:
+ l_catalog = len(catalog)
+ catalog = ctypes.c_char_p(catalog)
+ if schema != None:
+ l_schema = len(schema)
+ schema = ctypes.c_char_p(schema)
+ if procedure != None:
+ l_procedure = len(procedure)
+ procedure = ctypes.c_char_p(procedure)
+ if column != None:
+ l_column = len(column)
+ column = ctypes.c_char_p(column)
+
+
+ self._free_results('FREE_STATEMENT')
+ self.statement = None
+
+ ret = ODBC_API.SQLProcedureColumns(self._stmt_h,
+ catalog, l_catalog,
+ schema, l_schema,
+ procedure, l_procedure,
+ column, l_column)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self._NumOfRows()
+ self._UpdateDesc()
+ return (self)
+
+
+ def procedures(self, procedure=None, catalog=None, schema=None):
+ l_catalog = l_schema = l_procedure = 0
+ if catalog != None:
+ l_catalog = len(catalog)
+ catalog = ctypes.c_char_p(catalog)
+ if schema != None:
+ l_schema = len(schema)
+ schema = ctypes.c_char_p(schema)
+ if procedure != None:
+ l_procedure = len(procedure)
+ procedure = ctypes.c_char_p(procedure)
+
+
+ self._free_results('FREE_STATEMENT')
+ self.statement = None
+
+ ret = ODBC_API.SQLProcedures(self._stmt_h,
+ catalog, l_catalog,
+ schema, l_schema,
+ procedure, l_procedure)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self._NumOfRows()
+ self._UpdateDesc()
+ return (self)
+
+
+ def statistics(self, table, catalog=None, schema=None, unique=False, quick=True):
+ l_table = l_catalog = l_schema = 0
+
+ if catalog != None:
+ l_catalog = len(catalog)
+ catalog = ctypes.c_char_p(catalog)
+ if schema != None:
+ l_schema = len(schema)
+ schema = ctypes.c_char_p(schema)
+ if table != None:
+ l_table = len(table)
+ table = ctypes.c_char_p(table)
+
+ if unique:
+ Unique = SQL_INDEX_UNIQUE
+ else:
+ Unique = SQL_INDEX_ALL
+ if quick:
+ Reserved = SQL_QUICK
+ else:
+ Reserved = SQL_ENSURE
+
+ self._free_results('FREE_STATEMENT')
+ self.statement = None
+
+ ret = ODBC_API.SQLStatistics(self._stmt_h,
+ catalog, l_catalog,
+ schema, l_schema,
+ table, l_table,
+ Unique, Reserved)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self._NumOfRows()
+ self._UpdateDesc()
+ #self._BindCols()
+ return (self)
+
+
+ def commit(self):
+ self.connection.commit()
+
+ def rollback(self):
+ self.connection.rollback()
+
+ def setoutputsize(self, size, column = None):
+ self._outputsize[column] = size
+
+ def setinputsizes(self, sizes):
+ self._inputsizers = [size for size in sizes]
+
+
+ def close(self):
+ """ Call SQLCloseCursor API to free the statement handle"""
+# ret = ODBC_API.SQLCloseCursor(self._stmt_h)
+# validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+#
+ ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_CLOSE)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_UNBIND)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_RESET_PARAMS)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_STMT, self._stmt_h)
+ validate(ret, SQL_HANDLE_STMT, self._stmt_h)
+
+ self.closed = True
+
+
+
+ def __del__(self):
+ if not self.closed:
+ if DEBUG: print 'auto closing cursor: ',
+ try:
+ self.close()
+ except:
+ if DEBUG: print 'failed'
+ pass
+ else:
+ if DEBUG: print 'succeed'
+ pass
+
+ def __exit__(self, type, value, traceback):
+ if value:
+ self.rollback()
+ else:
+ self.commit()
+
+ self.close()
+
+
+ def __enter__(self):
+ return self
+
+
+# This class implement a odbc connection.
+#
+#
+
+class Connection:
+ def __init__(self, connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False, **kargs):
+ """Init variables and connect to the engine"""
+ self.connected = 0
+ self.type_size_dic = {}
+ self.unicode_results = False
+ self.dbc_h = ctypes.c_void_p()
+ self.autocommit = autocommit
+ self.readonly = False
+ self.timeout = 0
+
+ for key, value in kargs.items():
+ connectString = connectString + key + '=' + value + ';'
+ self.connectString = connectString
+
+
+ self.clear_output_converters()
+
+ with lock:
+ if shared_env_h == None:
+ #Initialize an enviroment if it is not created.
+ AllocateEnv()
+
+ # Allocate an DBC handle self.dbc_h under the environment shared_env_h
+ # This DBC handle is actually the basis of a "connection"
+ # The handle of self.dbc_h will be used to connect to a certain source
+ # in the self.connect and self.ConnectByDSN method
+
+ ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_DBC, shared_env_h, ADDR(self.dbc_h))
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+
+ self.connect(connectString, autocommit, ansi, timeout, unicode_results, readonly)
+
+
+
+ def connect(self, connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False):
+ """Connect to odbc, using connect strings and set the connection's attributes like autocommit and timeout
+ by calling SQLSetConnectAttr
+ """
+
+ # Before we establish the connection by the connection string
+ # Set the connection's attribute of "timeout" (Actully LOGIN_TIMEOUT)
+ if timeout != 0:
+ ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_LOGIN_TIMEOUT, timeout, SQL_IS_UINTEGER);
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+
+
+ # Create one connection with a connect string by calling SQLDriverConnect
+ # and make self.dbc_h the handle of this connection
+
+
+ # Convert the connetsytring to encoded string
+ # so it can be converted to a ctypes c_char array object
+
+
+
+ if not ansi:
+ c_connectString = wchar_type(to_unicode(self.connectString))
+ odbc_func = ODBC_API.SQLDriverConnectW
+ else:
+ c_connectString = ctypes.c_char_p(self.connectString)
+ odbc_func = ODBC_API.SQLDriverConnect
+
+ # With unixODBC, SQLDriverConnect will intermittently fail with error:
+ # [01000] [unixODBC][Driver Manager]Can't open lib '/path/to/so' : file not found"
+ # or:
+ # [01000] [unixODBC][Driver Manager]Can't open lib '/path/to/so' : (null)"
+ # when called concurrently by more than one threads. So, we have to
+ # use a lock to serialize the calls. By the way, the error is much
+ # less likely to happen if ODBC Tracing is enabled, likely due to the
+ # implicit serialization caused by writing to trace file.
+ if ODBC_API._name != 'odbc32':
+ with lock:
+ ret = odbc_func(self.dbc_h, 0, c_connectString, len(self.connectString), None, 0, None, SQL_DRIVER_NOPROMPT)
+ else:
+ ret = odbc_func(self.dbc_h, 0, c_connectString, len(self.connectString), None, 0, None, SQL_DRIVER_NOPROMPT)
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+
+
+ # Set the connection's attribute of "autocommit"
+ #
+ self.autocommit = autocommit
+
+ if self.autocommit == True:
+ ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER)
+ else:
+ ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER)
+
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+
+ # Set the connection's attribute of "readonly"
+ #
+ self.readonly = readonly
+
+ ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_ACCESS_MODE, self.readonly and SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE, SQL_IS_UINTEGER)
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+
+ self.unicode_results = unicode_results
+ self.update_type_size_info()
+ self.connected = 1
+
+ def clear_output_converters(self):
+ self.output_converter = {}
+ for sqltype, profile in SQL_data_type_dict.items():
+ self.output_converter[sqltype] = profile[1]
+
+
+ def add_output_converter(self, sqltype, func):
+ self.output_converter[sqltype] = func
+
+ def settimeout(self, timeout):
+ ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_CONNECTION_TIMEOUT, timeout, SQL_IS_UINTEGER);
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+ self.timeout = timeout
+
+
+ def ConnectByDSN(self, dsn, user, passwd = ''):
+ """Connect to odbc, we need dsn, user and optionally password"""
+ self.dsn = dsn
+ self.user = user
+ self.passwd = passwd
+
+ sn = create_buffer(dsn)
+ un = create_buffer(user)
+ pw = create_buffer(passwd)
+
+ ret = ODBC_API.SQLConnect(self.dbc_h, sn, len(sn), un, len(un), pw, len(pw))
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+
+ self.update_type_size_info()
+ self.connected = 1
+
+
+ def cursor(self, row_type_callable=None):
+ #self.settimeout(self.timeout)
+ if not self.connected:
+ raise ProgrammingError('HY000','Attempt to use a closed connection.')
+
+
+ return Cursor(self, row_type_callable=row_type_callable)
+
+ def update_type_size_info(self):
+ for sql_type in (
+ SQL_TYPE_TIMESTAMP,
+ SQL_TYPE_DATE,
+ SQL_TYPE_TIME,
+ SQL_SS_TIME2,
+ ):
+ cur = Cursor(self)
+ info_tuple = cur.getTypeInfo(sql_type)
+ if info_tuple != None:
+ self.type_size_dic[sql_type] = info_tuple[2], info_tuple[14]
+ cur.close()
+
+
+ def commit(self):
+ if not self.connected:
+ raise ProgrammingError('HY000','Attempt to use a closed connection.')
+
+ ret = ODBC_API.SQLEndTran(SQL_HANDLE_DBC, self.dbc_h, SQL_COMMIT);
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+
+ def rollback(self):
+ if not self.connected:
+ raise ProgrammingError('HY000','Attempt to use a closed connection.')
+
+ ret = ODBC_API.SQLEndTran(SQL_HANDLE_DBC, self.dbc_h, SQL_ROLLBACK);
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+
+
+
+ def getinfo(self,infotype):
+ if infotype not in aInfoTypes.keys():
+ raise ProgrammingError('HY000','Invalid getinfo value: '+str(infotype))
+
+
+ if aInfoTypes[infotype] == 'GI_UINTEGER':
+ total_buf_len = 1000
+ alloc_buffer = ctypes.c_ulong()
+ used_buf_len = ctypes.c_short()
+ ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\
+ ADDR(used_buf_len))
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+ result = alloc_buffer.value
+
+ elif aInfoTypes[infotype] == 'GI_USMALLINT':
+ total_buf_len = 1000
+ alloc_buffer = ctypes.c_ushort()
+ used_buf_len = ctypes.c_short()
+ ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\
+ ADDR(used_buf_len))
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+ result = alloc_buffer.value
+
+ else:
+ total_buf_len = 1000
+ alloc_buffer = create_buffer(total_buf_len)
+ used_buf_len = ctypes.c_short()
+ ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\
+ ADDR(used_buf_len))
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+ result = alloc_buffer.value
+ if aInfoTypes[infotype] == 'GI_YESNO':
+ if result[0] == 'Y':
+ result = True
+ else:
+ result = False
+
+ return result
+
+ def __exit__(self, type, value, traceback):
+ if value:
+ self.rollback()
+ else:
+ self.commit()
+
+ if self.connected:
+ self.close()
+
+ def __enter__(self):
+ return self
+
+ def __del__(self):
+ if self.connected:
+ self.close()
+
+ def close(self):
+ if not self.connected:
+ raise ProgrammingError('HY000','Attempt to close a closed connection.')
+
+
+ if self.connected:
+ if DEBUG: print 'disconnect'
+ if not self.autocommit:
+ self.rollback()
+ ret = ODBC_API.SQLDisconnect(self.dbc_h)
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+ if DEBUG: print 'free dbc'
+ ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_DBC, self.dbc_h)
+ validate(ret, SQL_HANDLE_DBC, self.dbc_h)
+# if shared_env_h.value:
+# if DEBUG: print 'env'
+# ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_ENV, shared_env_h)
+# validate(ret, SQL_HANDLE_ENV, shared_env_h)
+ self.connected = 0
+
+odbc = Connection
+connect = odbc
+'''
+def connect(connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False, **kargs):
+ return odbc(connectString, autocommit, ansi, timeout, unicode_results, readonly, kargs)
+'''
+
+def win_create_mdb(mdb_path, sort_order = "General\0\0"):
+ #CREATE_DB=
+ ctypes.windll.ODBCCP32.SQLConfigDataSource.argtypes = [ctypes.c_void_p,ctypes.c_ushort,ctypes.c_char_p,ctypes.c_char_p]
+ c_Path = "CREATE_DB=" + mdb_path + " " + sort_order
+ ODBC_ADD_SYS_DSN = 1
+ ret = ctypes.windll.ODBCCP32.SQLConfigDataSource(None,ODBC_ADD_SYS_DSN,"Microsoft Access Driver (*.mdb)", c_Path)
+ if not ret:
+ raise Exception('Failed to create Access mdb file. Please check file path, permission and Access driver readiness.')
+
+
+def win_compact_mdb(mdb_path, compacted_mdb_path, sort_order = "General\0\0"):
+ #COMPACT_DB=
+ c_Path = "COMPACT_DB=" + mdb_path + " " + compacted_mdb_path + " " + sort_order
+ ODBC_ADD_SYS_DSN = 1
+ ctypes.windll.ODBCCP32.SQLConfigDataSource.argtypes = [ctypes.c_void_p,ctypes.c_ushort,ctypes.c_char_p,ctypes.c_char_p]
+ ret = ctypes.windll.ODBCCP32.SQLConfigDataSource(None,ODBC_ADD_SYS_DSN,"Microsoft Access Driver (*.mdb)", c_Path)
+ if not ret:
+ raise Exception('Failed to compact Access mdb file. Please check file path, permission and Access driver readiness.')
+
+
+def dataSources():
+ """Return a list with [name, descrition]"""
+ dsn = create_buffer(1024)
+ desc = create_buffer(1024)
+ dsn_len = ctypes.c_short()
+ desc_len = ctypes.c_short()
+ dsn_list = {}
+ with lock:
+ if shared_env_h == None:
+ AllocateEnv()
+ while 1:
+ ret = ODBC_API.SQLDataSources(shared_env_h, SQL_FETCH_NEXT, \
+ dsn, len(dsn), ADDR(dsn_len), desc, len(desc), ADDR(desc_len))
+ if ret == SQL_NO_DATA_FOUND:
+ break
+ elif not ret in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO):
+ ctrl_err(SQL_HANDLE_ENV, shared_env_h, ret)
+ else:
+ dsn_list[dsn.value] = desc.value
+ return dsn_list
diff --git a/gluon/dal.py b/gluon/dal.py
index 6be400b8..839e3704 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -339,7 +339,10 @@ if not 'google' in DRIVERS:
LOGGER.debug('no Oracle driver cx_Oracle')
try:
+ #try:
import pyodbc
+ #except ImportError:
+ # from contrib.pypyodbc import pypyodbc as pyodbc
DRIVERS.append('MSSQL(pyodbc)')
DRIVERS.append('DB2(pyodbc)')
DRIVERS.append('Teradata(pyodbc)')
From 0460ffdb28615bfad8bb4cd019ed95dbb7f368e0 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Tue, 5 Feb 2013 08:49:58 -0600
Subject: [PATCH 29/39] added span and strong to allowed_tags
---
VERSION | 2 +-
gluon/html.py | 2 +-
gluon/sanitizer.py | 1 +
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index 6a617ef7..677fe642 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.05.08.45.51
+Version 2.4.1-alpha.2+timestamp.2013.02.05.08.49.14
diff --git a/gluon/html.py b/gluon/html.py
index 35abdb82..63148cd8 100644
--- a/gluon/html.py
+++ b/gluon/html.py
@@ -553,7 +553,7 @@ class XML(XmlComponent):
'img/',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'table', 'tr', 'td', 'div',
- 'strong',
+ 'strong','span',
],
allowed_attributes={
'a': ['href', 'title', 'target'],
diff --git a/gluon/sanitizer.py b/gluon/sanitizer.py
index 37433361..7a503e8c 100644
--- a/gluon/sanitizer.py
+++ b/gluon/sanitizer.py
@@ -212,6 +212,7 @@ def sanitize(text, permitted_tags=[
'img/',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'table', 'tr', 'td', 'div',
+ 'strong', 'span',
],
allowed_attributes={
'a': ['href', 'title'],
From 409495cc702e7a1fa6da5216bd520d065539a04e Mon Sep 17 00:00:00 2001
From: Massimo
Date: Tue, 5 Feb 2013 10:44:16 -0600
Subject: [PATCH 30/39] removed pypyodbc test
---
VERSION | 2 +-
gluon/contrib/pypyodbc.py | 2338 -------------------------------------
2 files changed, 1 insertion(+), 2339 deletions(-)
delete mode 100644 gluon/contrib/pypyodbc.py
diff --git a/VERSION b/VERSION
index 677fe642..b6c2fb80 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.05.08.49.14
+Version 2.4.1-alpha.2+timestamp.2013.02.05.10.43.24
diff --git a/gluon/contrib/pypyodbc.py b/gluon/contrib/pypyodbc.py
deleted file mode 100644
index d151ad25..00000000
--- a/gluon/contrib/pypyodbc.py
+++ /dev/null
@@ -1,2338 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# PyPyODBC is develped from RealPyODBC 0.1 beta released in 2004 by Michele Petrazzo. Thanks Michele.
-
-# The MIT License (MIT)
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
-# documentation files (the "Software"), to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
-# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all copies or substantial portions
-# of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
-# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO #EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
-# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-
-from __future__ import with_statement
-
-import sys, os, datetime, ctypes, threading
-from decimal import Decimal
-
-try:
- bytearray
-except NameError:
- # pre version 2.6 python does not have the bytearray type
- bytearray = str
-
-if not hasattr(ctypes, 'c_ssize_t'):
- if ctypes.sizeof(ctypes.c_uint) == ctypes.sizeof(ctypes.c_void_p):
- ctypes.c_ssize_t = ctypes.c_int
- elif ctypes.sizeof(ctypes.c_ulong) == ctypes.sizeof(ctypes.c_void_p):
- ctypes.c_ssize_t = ctypes.c_long
- elif ctypes.sizeof(ctypes.c_ulonglong) == ctypes.sizeof(ctypes.c_void_p):
- ctypes.c_ssize_t = ctypes.c_longlong
-
-DEBUG = 0
-# Comment out all "if DEBUG:" statements like below for production
-if DEBUG: print 'DEBUGGING'
-
-pooling = True
-lock = threading.Lock()
-shared_env_h = None
-apilevel = '2.0'
-paramstyle = 'qmark'
-threadsafety = 1
-version = '0.9.1'
-lowercase=True
-SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_wchar)
-
-#determin the size of Py_UNICODE
-#sys.maxunicode > 65536 and 'UCS4' or 'UCS2'
-UNICODE_SIZE = sys.maxunicode > 65536 and 4 or 2
-
-
-# Define ODBC constants. They are widly used in ODBC documents and programs
-# They are defined in cpp header files: sql.h sqlext.h sqltypes.h sqlucode.h
-# and you can get these files from the mingw32-runtime_3.13-1_all.deb package
-SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC2, SQL_OV_ODBC3 = 200, 2, 3
-SQL_DRIVER_NOPROMPT = 0
-SQL_ATTR_CONNECTION_POOLING = 201; SQL_CP_ONE_PER_HENV = 2
-
-SQL_FETCH_NEXT, SQL_FETCH_FIRST, SQL_FETCH_LAST = 0x01, 0x02, 0x04
-SQL_NULL_HANDLE, SQL_HANDLE_ENV, SQL_HANDLE_DBC, SQL_HANDLE_STMT = 0, 1, 2, 3
-SQL_SUCCESS, SQL_SUCCESS_WITH_INFO = 0, 1
-SQL_NO_DATA = 100; SQL_NO_TOTAL = -4
-SQL_ATTR_ACCESS_MODE = SQL_ACCESS_MODE = 101
-SQL_ATTR_AUTOCOMMIT = SQL_AUTOCOMMIT = 102
-
-SQL_MODE_DEFAULT = SQL_MODE_READ_WRITE = 0; SQL_MODE_READ_ONLY = 1
-SQL_AUTOCOMMIT_OFF, SQL_AUTOCOMMIT_ON = 0, 1
-SQL_IS_UINTEGER = -5
-SQL_ATTR_LOGIN_TIMEOUT = 103; SQL_ATTR_CONNECTION_TIMEOUT = 113
-SQL_COMMIT, SQL_ROLLBACK = 0, 1
-
-SQL_INDEX_UNIQUE,SQL_INDEX_ALL = 0,1
-SQL_QUICK,SQL_ENSURE = 0,1
-SQL_FETCH_NEXT = 1
-SQL_COLUMN_DISPLAY_SIZE = 6
-SQL_INVALID_HANDLE = -2
-SQL_NO_DATA_FOUND = 100; SQL_NULL_DATA = -1; SQL_NTS = -3
-SQL_HANDLE_DESCR = 4
-SQL_TABLE_NAMES = 3
-SQL_PARAM_INPUT = 1; SQL_PARAM_INPUT_OUTPUT = 2
-SQL_PARAM_TYPE_UNKNOWN = 0
-SQL_RESULT_COL = 3
-SQL_PARAM_OUTPUT = 4
-SQL_RETURN_VALUE = 5
-SQL_PARAM_TYPE_DEFAULT = SQL_PARAM_INPUT_OUTPUT
-
-SQL_RESET_PARAMS = 3
-SQL_UNBIND = 2
-SQL_CLOSE = 0
-
-SQL_TYPE_NULL = 0
-SQL_DECIMAL = 3
-SQL_FLOAT = 6
-SQL_DATE = 9
-SQL_TIME = 10
-SQL_TIMESTAMP = 11
-SQL_VARCHAR = 12
-SQL_LONGVARCHAR = -1
-SQL_VARBINARY = -3
-SQL_LONGVARBINARY = -4
-SQL_BIGINT = -5
-SQL_WVARCHAR = -9
-SQL_WLONGVARCHAR = -10
-SQL_ALL_TYPES = 0
-SQL_SIGNED_OFFSET = -20
-
-SQL_C_CHAR = SQL_CHAR = 1
-SQL_C_NUMERIC = SQL_NUMERIC = 2
-SQL_C_LONG = SQL_INTEGER = 4
-SQL_C_SLONG = SQL_C_LONG + SQL_SIGNED_OFFSET
-SQL_C_SHORT = SQL_SMALLINT = 5
-SQL_C_FLOAT = SQL_REAL = 7
-SQL_C_DOUBLE = SQL_DOUBLE = 8
-SQL_C_TYPE_DATE = SQL_TYPE_DATE = 91
-SQL_C_TYPE_TIME = SQL_TYPE_TIME = 92
-SQL_C_BINARY = SQL_BINARY = -2
-SQL_C_SBIGINT = SQL_BIGINT + SQL_SIGNED_OFFSET
-SQL_C_TINYINT = SQL_TINYINT = -6
-SQL_C_BIT = SQL_BIT = -7
-SQL_C_WCHAR = SQL_WCHAR = -8
-SQL_C_GUID = SQL_GUID = -11
-SQL_C_TYPE_TIMESTAMP = SQL_TYPE_TIMESTAMP = 93
-SQL_C_DEFAULT = 99
-
-SQL_SS_TIME2 = -154
-
-SQL_DESC_DISPLAY_SIZE = SQL_COLUMN_DISPLAY_SIZE
-
-
-def dttm_cvt(x):
- if x == '': return None
- else: return datetime.datetime(int(x[0:4]),int(x[5:7]),int(x[8:10]),int(x[10:13]),int(x[14:16]),int(x[17:19]),int(x[20:].ljust(6,'0')))
-
-def tm_cvt(x):
- if x == '': return None
- else: return datetime.time(int(x[0:2]),int(x[3:5]),int(x[6:8]),int(x[9:].ljust(6,'0')))
-
-def dt_cvt(x):
- if x == '': return None
- else: return datetime.date(int(x[0:4]),int(x[5:7]),int(x[8:10]))
-
-
-# Below defines The constants for sqlgetinfo method, and their coresponding return types
-SQL_QUALIFIER_LOCATION = 114
-SQL_QUALIFIER_NAME_SEPARATOR = 41
-SQL_QUALIFIER_TERM = 42
-SQL_QUALIFIER_USAGE = 92
-SQL_OWNER_TERM = 39
-SQL_OWNER_USAGE = 91
-SQL_ACCESSIBLE_PROCEDURES = 20
-SQL_ACCESSIBLE_TABLES = 19
-SQL_ACTIVE_ENVIRONMENTS = 116
-SQL_AGGREGATE_FUNCTIONS = 169
-SQL_ALTER_DOMAIN = 117
-SQL_ALTER_TABLE = 86
-SQL_ASYNC_MODE = 10021
-SQL_BATCH_ROW_COUNT = 120
-SQL_BATCH_SUPPORT = 121
-SQL_BOOKMARK_PERSISTENCE = 82
-SQL_CATALOG_LOCATION = SQL_QUALIFIER_LOCATION
-SQL_CATALOG_NAME = 10003
-SQL_CATALOG_NAME_SEPARATOR = SQL_QUALIFIER_NAME_SEPARATOR
-SQL_CATALOG_TERM = SQL_QUALIFIER_TERM
-SQL_CATALOG_USAGE = SQL_QUALIFIER_USAGE
-SQL_COLLATION_SEQ = 10004
-SQL_COLUMN_ALIAS = 87
-SQL_CONCAT_NULL_BEHAVIOR = 22
-SQL_CONVERT_FUNCTIONS = 48
-SQL_CONVERT_VARCHAR = 70
-SQL_CORRELATION_NAME = 74
-SQL_CREATE_ASSERTION = 127
-SQL_CREATE_CHARACTER_SET = 128
-SQL_CREATE_COLLATION = 129
-SQL_CREATE_DOMAIN = 130
-SQL_CREATE_SCHEMA = 131
-SQL_CREATE_TABLE = 132
-SQL_CREATE_TRANSLATION = 133
-SQL_CREATE_VIEW = 134
-SQL_CURSOR_COMMIT_BEHAVIOR = 23
-SQL_CURSOR_ROLLBACK_BEHAVIOR = 24
-SQL_DATABASE_NAME = 16
-SQL_DATA_SOURCE_NAME = 2
-SQL_DATA_SOURCE_READ_ONLY = 25
-SQL_DATETIME_LITERALS = 119
-SQL_DBMS_NAME = 17
-SQL_DBMS_VER = 18
-SQL_DDL_INDEX = 170
-SQL_DEFAULT_TXN_ISOLATION = 26
-SQL_DESCRIBE_PARAMETER = 10002
-SQL_DM_VER = 171
-SQL_DRIVER_NAME = 6
-SQL_DRIVER_ODBC_VER = 77
-SQL_DRIVER_VER = 7
-SQL_DROP_ASSERTION = 136
-SQL_DROP_CHARACTER_SET = 137
-SQL_DROP_COLLATION = 138
-SQL_DROP_DOMAIN = 139
-SQL_DROP_SCHEMA = 140
-SQL_DROP_TABLE = 141
-SQL_DROP_TRANSLATION = 142
-SQL_DROP_VIEW = 143
-SQL_DYNAMIC_CURSOR_ATTRIBUTES1 = 144
-SQL_DYNAMIC_CURSOR_ATTRIBUTES2 = 145
-SQL_EXPRESSIONS_IN_ORDERBY = 27
-SQL_FILE_USAGE = 84
-SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 = 146
-SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 = 147
-SQL_GETDATA_EXTENSIONS = 81
-SQL_GROUP_BY = 88
-SQL_IDENTIFIER_CASE = 28
-SQL_IDENTIFIER_QUOTE_CHAR = 29
-SQL_INDEX_KEYWORDS = 148
-SQL_INFO_SCHEMA_VIEWS = 149
-SQL_INSERT_STATEMENT = 172
-SQL_INTEGRITY = 73
-SQL_KEYSET_CURSOR_ATTRIBUTES1 = 150
-SQL_KEYSET_CURSOR_ATTRIBUTES2 = 151
-SQL_KEYWORDS = 89
-SQL_LIKE_ESCAPE_CLAUSE = 113
-SQL_MAX_ASYNC_CONCURRENT_STATEMENTS = 10022
-SQL_MAX_BINARY_LITERAL_LEN = 112
-SQL_MAX_CATALOG_NAME_LEN = 34
-SQL_MAX_CHAR_LITERAL_LEN = 108
-SQL_MAX_COLUMNS_IN_GROUP_BY = 97
-SQL_MAX_COLUMNS_IN_INDEX = 98
-SQL_MAX_COLUMNS_IN_ORDER_BY = 99
-SQL_MAX_COLUMNS_IN_SELECT = 100
-SQL_MAX_COLUMNS_IN_TABLE = 101
-SQL_MAX_COLUMN_NAME_LEN = 30
-SQL_MAX_CONCURRENT_ACTIVITIES = 1
-SQL_MAX_CURSOR_NAME_LEN = 31
-SQL_MAX_DRIVER_CONNECTIONS = 0
-SQL_MAX_IDENTIFIER_LEN = 10005
-SQL_MAX_INDEX_SIZE = 102
-SQL_MAX_PROCEDURE_NAME_LEN = 33
-SQL_MAX_ROW_SIZE = 104
-SQL_MAX_ROW_SIZE_INCLUDES_LONG = 103
-SQL_MAX_SCHEMA_NAME_LEN = 32
-SQL_MAX_STATEMENT_LEN = 105
-SQL_MAX_TABLES_IN_SELECT = 106
-SQL_MAX_TABLE_NAME_LEN = 35
-SQL_MAX_USER_NAME_LEN = 107
-SQL_MULTIPLE_ACTIVE_TXN = 37
-SQL_MULT_RESULT_SETS = 36
-SQL_NEED_LONG_DATA_LEN = 111
-SQL_NON_NULLABLE_COLUMNS = 75
-SQL_NULL_COLLATION = 85
-SQL_NUMERIC_FUNCTIONS = 49
-SQL_ODBC_INTERFACE_CONFORMANCE = 152
-SQL_ODBC_VER = 10
-SQL_OJ_CAPABILITIES = 65003
-SQL_ORDER_BY_COLUMNS_IN_SELECT = 90
-SQL_PARAM_ARRAY_ROW_COUNTS = 153
-SQL_PARAM_ARRAY_SELECTS = 154
-SQL_PROCEDURES = 21
-SQL_PROCEDURE_TERM = 40
-SQL_QUOTED_IDENTIFIER_CASE = 93
-SQL_ROW_UPDATES = 11
-SQL_SCHEMA_TERM = SQL_OWNER_TERM
-SQL_SCHEMA_USAGE = SQL_OWNER_USAGE
-SQL_SCROLL_OPTIONS = 44
-SQL_SEARCH_PATTERN_ESCAPE = 14
-SQL_SERVER_NAME = 13
-SQL_SPECIAL_CHARACTERS = 94
-SQL_SQL92_DATETIME_FUNCTIONS = 155
-SQL_SQL92_FOREIGN_KEY_DELETE_RULE = 156
-SQL_SQL92_FOREIGN_KEY_UPDATE_RULE = 157
-SQL_SQL92_GRANT = 158
-SQL_SQL92_NUMERIC_VALUE_FUNCTIONS = 159
-SQL_SQL92_PREDICATES = 160
-SQL_SQL92_RELATIONAL_JOIN_OPERATORS = 161
-SQL_SQL92_REVOKE = 162
-SQL_SQL92_ROW_VALUE_CONSTRUCTOR = 163
-SQL_SQL92_STRING_FUNCTIONS = 164
-SQL_SQL92_VALUE_EXPRESSIONS = 165
-SQL_SQL_CONFORMANCE = 118
-SQL_STANDARD_CLI_CONFORMANCE = 166
-SQL_STATIC_CURSOR_ATTRIBUTES1 = 167
-SQL_STATIC_CURSOR_ATTRIBUTES2 = 168
-SQL_STRING_FUNCTIONS = 50
-SQL_SUBQUERIES = 95
-SQL_SYSTEM_FUNCTIONS = 51
-SQL_TABLE_TERM = 45
-SQL_TIMEDATE_ADD_INTERVALS = 109
-SQL_TIMEDATE_DIFF_INTERVALS = 110
-SQL_TIMEDATE_FUNCTIONS = 52
-SQL_TXN_CAPABLE = 46
-SQL_TXN_ISOLATION_OPTION = 72
-SQL_UNION = 96
-SQL_USER_NAME = 47
-SQL_XOPEN_CLI_YEAR = 10000
-
-
-aInfoTypes = {
-SQL_ACCESSIBLE_PROCEDURES : 'GI_YESNO',SQL_ACCESSIBLE_TABLES : 'GI_YESNO',SQL_ACTIVE_ENVIRONMENTS : 'GI_USMALLINT',
-SQL_AGGREGATE_FUNCTIONS : 'GI_UINTEGER',SQL_ALTER_DOMAIN : 'GI_UINTEGER',
-SQL_ALTER_TABLE : 'GI_UINTEGER',SQL_ASYNC_MODE : 'GI_UINTEGER',SQL_BATCH_ROW_COUNT : 'GI_UINTEGER',
-SQL_BATCH_SUPPORT : 'GI_UINTEGER',SQL_BOOKMARK_PERSISTENCE : 'GI_UINTEGER',SQL_CATALOG_LOCATION : 'GI_USMALLINT',
-SQL_CATALOG_NAME : 'GI_YESNO',SQL_CATALOG_NAME_SEPARATOR : 'GI_STRING',SQL_CATALOG_TERM : 'GI_STRING',
-SQL_CATALOG_USAGE : 'GI_UINTEGER',SQL_COLLATION_SEQ : 'GI_STRING',SQL_COLUMN_ALIAS : 'GI_YESNO',
-SQL_CONCAT_NULL_BEHAVIOR : 'GI_USMALLINT',SQL_CONVERT_FUNCTIONS : 'GI_UINTEGER',
-SQL_CONVERT_VARCHAR : 'GI_UINTEGER',SQL_CORRELATION_NAME : 'GI_USMALLINT',
-SQL_CREATE_ASSERTION : 'GI_UINTEGER',SQL_CREATE_CHARACTER_SET : 'GI_UINTEGER',
-SQL_CREATE_COLLATION : 'GI_UINTEGER',SQL_CREATE_DOMAIN : 'GI_UINTEGER',SQL_CREATE_SCHEMA : 'GI_UINTEGER',
-SQL_CREATE_TABLE : 'GI_UINTEGER',SQL_CREATE_TRANSLATION : 'GI_UINTEGER',SQL_CREATE_VIEW : 'GI_UINTEGER',
-SQL_CURSOR_COMMIT_BEHAVIOR : 'GI_USMALLINT',SQL_CURSOR_ROLLBACK_BEHAVIOR : 'GI_USMALLINT',SQL_DATABASE_NAME : 'GI_STRING',
-SQL_DATA_SOURCE_NAME : 'GI_STRING',SQL_DATA_SOURCE_READ_ONLY : 'GI_YESNO',SQL_DATETIME_LITERALS : 'GI_UINTEGER',
-SQL_DBMS_NAME : 'GI_STRING',SQL_DBMS_VER : 'GI_STRING',SQL_DDL_INDEX : 'GI_UINTEGER',
-SQL_DEFAULT_TXN_ISOLATION : 'GI_UINTEGER',SQL_DESCRIBE_PARAMETER : 'GI_YESNO',SQL_DM_VER : 'GI_STRING',
-SQL_DRIVER_NAME : 'GI_STRING',SQL_DRIVER_ODBC_VER : 'GI_STRING',SQL_DRIVER_VER : 'GI_STRING',
-SQL_DROP_ASSERTION : 'GI_UINTEGER',SQL_DROP_CHARACTER_SET : 'GI_UINTEGER',
-SQL_DROP_COLLATION : 'GI_UINTEGER',SQL_DROP_DOMAIN : 'GI_UINTEGER',
-SQL_DROP_SCHEMA : 'GI_UINTEGER',SQL_DROP_TABLE : 'GI_UINTEGER',SQL_DROP_TRANSLATION : 'GI_UINTEGER',
-SQL_DROP_VIEW : 'GI_UINTEGER',SQL_DYNAMIC_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_DYNAMIC_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',
-SQL_EXPRESSIONS_IN_ORDERBY : 'GI_YESNO',SQL_FILE_USAGE : 'GI_USMALLINT',
-SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',
-SQL_GETDATA_EXTENSIONS : 'GI_UINTEGER',SQL_GROUP_BY : 'GI_USMALLINT',SQL_IDENTIFIER_CASE : 'GI_USMALLINT',
-SQL_IDENTIFIER_QUOTE_CHAR : 'GI_STRING',SQL_INDEX_KEYWORDS : 'GI_UINTEGER',SQL_INFO_SCHEMA_VIEWS : 'GI_UINTEGER',
-SQL_INSERT_STATEMENT : 'GI_UINTEGER',SQL_INTEGRITY : 'GI_YESNO',SQL_KEYSET_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',
-SQL_KEYSET_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',SQL_KEYWORDS : 'GI_STRING',
-SQL_LIKE_ESCAPE_CLAUSE : 'GI_YESNO',SQL_MAX_ASYNC_CONCURRENT_STATEMENTS : 'GI_UINTEGER',
-SQL_MAX_BINARY_LITERAL_LEN : 'GI_UINTEGER',SQL_MAX_CATALOG_NAME_LEN : 'GI_USMALLINT',
-SQL_MAX_CHAR_LITERAL_LEN : 'GI_UINTEGER',SQL_MAX_COLUMNS_IN_GROUP_BY : 'GI_USMALLINT',
-SQL_MAX_COLUMNS_IN_INDEX : 'GI_USMALLINT',SQL_MAX_COLUMNS_IN_ORDER_BY : 'GI_USMALLINT',
-SQL_MAX_COLUMNS_IN_SELECT : 'GI_USMALLINT',SQL_MAX_COLUMNS_IN_TABLE : 'GI_USMALLINT',
-SQL_MAX_COLUMN_NAME_LEN : 'GI_USMALLINT',SQL_MAX_CONCURRENT_ACTIVITIES : 'GI_USMALLINT',
-SQL_MAX_CURSOR_NAME_LEN : 'GI_USMALLINT',SQL_MAX_DRIVER_CONNECTIONS : 'GI_USMALLINT',
-SQL_MAX_IDENTIFIER_LEN : 'GI_USMALLINT',SQL_MAX_INDEX_SIZE : 'GI_UINTEGER',
-SQL_MAX_PROCEDURE_NAME_LEN : 'GI_USMALLINT',SQL_MAX_ROW_SIZE : 'GI_UINTEGER',
-SQL_MAX_ROW_SIZE_INCLUDES_LONG : 'GI_YESNO',SQL_MAX_SCHEMA_NAME_LEN : 'GI_USMALLINT',
-SQL_MAX_STATEMENT_LEN : 'GI_UINTEGER',SQL_MAX_TABLES_IN_SELECT : 'GI_USMALLINT',
-SQL_MAX_TABLE_NAME_LEN : 'GI_USMALLINT',SQL_MAX_USER_NAME_LEN : 'GI_USMALLINT',
-SQL_MULTIPLE_ACTIVE_TXN : 'GI_YESNO',SQL_MULT_RESULT_SETS : 'GI_YESNO',
-SQL_NEED_LONG_DATA_LEN : 'GI_YESNO',SQL_NON_NULLABLE_COLUMNS : 'GI_USMALLINT',
-SQL_NULL_COLLATION : 'GI_USMALLINT',SQL_NUMERIC_FUNCTIONS : 'GI_UINTEGER',
-SQL_ODBC_INTERFACE_CONFORMANCE : 'GI_UINTEGER',SQL_ODBC_VER : 'GI_STRING',SQL_OJ_CAPABILITIES : 'GI_UINTEGER',
-SQL_ORDER_BY_COLUMNS_IN_SELECT : 'GI_YESNO',SQL_PARAM_ARRAY_ROW_COUNTS : 'GI_UINTEGER',
-SQL_PARAM_ARRAY_SELECTS : 'GI_UINTEGER',SQL_PROCEDURES : 'GI_YESNO',SQL_PROCEDURE_TERM : 'GI_STRING',
-SQL_QUOTED_IDENTIFIER_CASE : 'GI_USMALLINT',SQL_ROW_UPDATES : 'GI_YESNO',SQL_SCHEMA_TERM : 'GI_STRING',
-SQL_SCHEMA_USAGE : 'GI_UINTEGER',SQL_SCROLL_OPTIONS : 'GI_UINTEGER',SQL_SEARCH_PATTERN_ESCAPE : 'GI_STRING',
-SQL_SERVER_NAME : 'GI_STRING',SQL_SPECIAL_CHARACTERS : 'GI_STRING',SQL_SQL92_DATETIME_FUNCTIONS : 'GI_UINTEGER',
-SQL_SQL92_FOREIGN_KEY_DELETE_RULE : 'GI_UINTEGER',SQL_SQL92_FOREIGN_KEY_UPDATE_RULE : 'GI_UINTEGER',
-SQL_SQL92_GRANT : 'GI_UINTEGER',SQL_SQL92_NUMERIC_VALUE_FUNCTIONS : 'GI_UINTEGER',
-SQL_SQL92_PREDICATES : 'GI_UINTEGER',SQL_SQL92_RELATIONAL_JOIN_OPERATORS : 'GI_UINTEGER',
-SQL_SQL92_REVOKE : 'GI_UINTEGER',SQL_SQL92_ROW_VALUE_CONSTRUCTOR : 'GI_UINTEGER',
-SQL_SQL92_STRING_FUNCTIONS : 'GI_UINTEGER',SQL_SQL92_VALUE_EXPRESSIONS : 'GI_UINTEGER',
-SQL_SQL_CONFORMANCE : 'GI_UINTEGER',SQL_STANDARD_CLI_CONFORMANCE : 'GI_UINTEGER',
-SQL_STATIC_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_STATIC_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',
-SQL_STRING_FUNCTIONS : 'GI_UINTEGER',SQL_SUBQUERIES : 'GI_UINTEGER',
-SQL_SYSTEM_FUNCTIONS : 'GI_UINTEGER',SQL_TABLE_TERM : 'GI_STRING',SQL_TIMEDATE_ADD_INTERVALS : 'GI_UINTEGER',
-SQL_TIMEDATE_DIFF_INTERVALS : 'GI_UINTEGER',SQL_TIMEDATE_FUNCTIONS : 'GI_UINTEGER',
-SQL_TXN_CAPABLE : 'GI_USMALLINT',SQL_TXN_ISOLATION_OPTION : 'GI_UINTEGER',
-SQL_UNION : 'GI_UINTEGER',SQL_USER_NAME : 'GI_STRING',SQL_XOPEN_CLI_YEAR : 'GI_STRING',
-}
-
-#Definations for types
-BINARY = bytearray
-Binary = bytearray
-DATETIME = datetime.datetime
-Date = datetime.date
-Time = datetime.time
-Timestamp = datetime.datetime
-STRING = str
-NUMBER = float
-ROWID = int
-DateFromTicks = datetime.date.fromtimestamp
-TimeFromTicks = lambda x: datetime.datetime.fromtimestamp(x).time()
-TimestampFromTicks = datetime.datetime.fromtimestamp
-
-
-#Define exceptions
-class OdbcNoLibrary(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return repr(self.value)
-class OdbcLibraryError(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return repr(self.value)
-class OdbcInvalidHandle(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return repr(self.value)
-class OdbcGenericError(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return repr(self.value)
-
-
-class Warning(StandardError):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-
-class Error(StandardError):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-class InterfaceError(Error):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-
-class DatabaseError(Error):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-
-class InternalError(DatabaseError):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-
-class ProgrammingError(DatabaseError):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-class DataError(DatabaseError):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-class IntegrityError(DatabaseError):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-class NotSupportedError(Error):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-class OperationalError(DatabaseError):
- def __init__(self, error_code, error_desc):
- self.value = (error_code, error_desc)
- self.args = (error_code, error_desc)
-
-
-
-# Get the References of the platform's ODBC functions via ctypes
-if sys.platform in ('win32','cli'):
- ODBC_API = ctypes.windll.odbc32
- # On Windows, the size of SQLWCHAR is hardcoded to 2-bytes.
- SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_ushort)
-else:
- # Set the library location on linux
- lib_paths = ("/usr/lib/libodbc.so","/usr/lib/i386-linux-gnu/libodbc.so","/usr/lib/x86_64-linux-gnu/libodbc.so")
- lib_paths = [path for path in lib_paths if os.path.exists(path)]
- if len(lib_paths) == 0 :
- raise OdbcNoLibrary, 'ODBC Library is not found'
- library = lib_paths[0]
- try:
- ODBC_API = ctypes.cdll.LoadLibrary(library)
- except:
- raise OdbcLibraryError, 'Error while loading %s' % library
-
- # unixODBC defaults to 2-bytes SQLWCHAR, unless "-DSQL_WCHART_CONVERT" was
- # added to CFLAGS, in which case it will be the size of wchar_t.
- # Note that using 4-bytes SQLWCHAR will break most ODBC drivers, as driver
- # development mostly targets the Windows platform.
- import commands
- status, output = commands.getstatusoutput('odbc_config --cflags')
- if status == 0 and 'SQL_WCHART_CONVERT' in output:
- SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_wchar)
- else:
- SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_ushort)
-
-
-create_buffer_u = ctypes.create_unicode_buffer
-create_buffer = ctypes.create_string_buffer
-wchar_type = ctypes.c_wchar_p
-to_unicode = lambda s: s
-from_buffer_u = lambda buffer: buffer.value
-
-# This is the common case on Linux, which uses wide Python build together with
-# the default unixODBC without the "-DSQL_WCHART_CONVERT" CFLAGS.
-if UNICODE_SIZE > SQLWCHAR_SIZE:
- # We can only use unicode buffer if the size of wchar_t (UNICODE_SIZE) is
- # the same as the size expected by the driver manager (SQLWCHAR_SIZE).
- create_buffer_u = create_buffer
- wchar_type = ctypes.c_char_p
-
- def to_unicode(s):
- return s.encode('UTF-16LE')
-
- def from_buffer_u(buffer):
- i = 0
- uchars = []
- while True:
- uchar = buffer.raw[i:i + 2].decode('UTF-16')
- if uchar == u'\x00':
- break
- uchars.append(uchar)
- i += 2
- return ''.join(uchars)
-
-# Exoteric case, don't really care.
-elif UNICODE_SIZE < SQLWCHAR_SIZE:
- raise OdbcLibraryError('Using narrow Python build with ODBC library '
- 'expecting wide unicode is not supported.')
-
-
-# Below Datatype mappings referenced the document at
-# http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.sdk_12.5.1.aseodbc/html/aseodbc/CACFDIGH.htm
-
-
-SQL_data_type_dict = { \
-#SQL Data TYPE 0.Python Data Type 1.Default Output Converter 2.Buffer Type 3.Buffer Allocator 4.Default Buffer Size
-SQL_TYPE_NULL : (None, lambda x: None, SQL_C_CHAR, create_buffer, 2 ),
-SQL_CHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ),
-SQL_NUMERIC : (Decimal, Decimal, SQL_C_CHAR, create_buffer, 150 ),
-SQL_DECIMAL : (Decimal, Decimal, SQL_C_CHAR, create_buffer, 150 ),
-SQL_INTEGER : (int, int, SQL_C_CHAR, create_buffer, 150 ),
-SQL_SMALLINT : (int, int, SQL_C_CHAR, create_buffer, 150 ),
-SQL_FLOAT : (float, float, SQL_C_CHAR, create_buffer, 150 ),
-SQL_REAL : (float, float, SQL_C_CHAR, create_buffer, 150 ),
-SQL_DOUBLE : (float, float, SQL_C_CHAR, create_buffer, 200 ),
-SQL_DATE : (datetime.date, dt_cvt, SQL_C_CHAR , create_buffer, 30 ),
-SQL_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ),
-SQL_SS_TIME2 : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ),
-SQL_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ),
-SQL_VARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ),
-SQL_LONGVARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 20500 ),
-SQL_BINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 5120 ),
-SQL_VARBINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 5120 ),
-SQL_LONGVARBINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 20500 ),
-SQL_BIGINT : (long, long, SQL_C_CHAR, create_buffer, 150 ),
-SQL_TINYINT : (int, int, SQL_C_CHAR, create_buffer, 150 ),
-SQL_BIT : (bool, lambda x:x=='1', SQL_C_CHAR, create_buffer, 2 ),
-SQL_WCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ),
-SQL_WVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ),
-SQL_GUID : (str, str, SQL_C_CHAR, create_buffer, 50 ),
-SQL_WLONGVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 20500 ),
-SQL_TYPE_DATE : (datetime.date, dt_cvt, SQL_C_CHAR, create_buffer, 30 ),
-SQL_TYPE_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ),
-SQL_TYPE_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ),
-}
-
-
-"""
-Types mapping, applicable for 32-bit and 64-bit Linux / Windows / Mac OS X.
-
-SQLPointer -> ctypes.c_void_p
-SQLCHAR * -> ctypes.c_char_p
-SQLWCHAR * -> ctypes.c_wchar_p on Windows, ctypes.c_char_p with unixODBC
-SQLINT -> ctypes.c_int
-SQLSMALLINT -> ctypes.c_short
-SQMUSMALLINT -> ctypes.c_ushort
-SQLLEN -> ctypes.c_ssize_t
-SQLULEN -> ctypes.c_size_t
-SQLRETURN -> ctypes.c_short
-"""
-
-# Define the python return type for ODBC functions with ret result.
-funcs_with_ret = [
- "SQLAllocHandle",
- "SQLBindParameter",
- "SQLCloseCursor",
- "SQLColAttribute",
- "SQLColumns",
- "SQLColumnsW",
- "SQLConnect",
- "SQLConnectW",
- "SQLDataSources",
- "SQLDataSourcesW",
- "SQLDescribeCol",
- "SQLDescribeColW",
- "SQLDescribeParam",
- "SQLDisconnect",
- "SQLDriverConnect",
- "SQLDriverConnectW",
- "SQLEndTran",
- "SQLExecDirect",
- "SQLExecDirectW",
- "SQLExecute",
- "SQLFetch",
- "SQLFetchScroll",
- "SQLForeignKeys",
- "SQLForeignKeysW",
- "SQLFreeHandle",
- "SQLFreeStmt",
- "SQLGetData",
- "SQLGetDiagRec",
- "SQLGetInfo",
- "SQLGetTypeInfo",
- "SQLMoreResults",
- "SQLNumParams",
- "SQLNumResultCols",
- "SQLPrepare",
- "SQLPrepareW",
- "SQLPrimaryKeys",
- "SQLPrimaryKeysW",
- "SQLProcedureColumns",
- "SQLProcedureColumnsW",
- "SQLProcedures",
- "SQLProceduresW",
- "SQLRowCount",
- "SQLSetConnectAttr",
- "SQLSetEnvAttr",
- "SQLStatistics",
- "SQLStatisticsW",
- "SQLTables",
- "SQLTablesW",
-]
-
-for func_name in funcs_with_ret:
- getattr(ODBC_API, func_name).restype = ctypes.c_short
-
-if sys.platform not in ('cli'):
- #Seems like the IronPython can not declare ctypes.POINTER type arguments
- ODBC_API.SQLAllocHandle.argtypes = [
- ctypes.c_short,
- ctypes.c_void_p,
- ctypes.POINTER(ctypes.c_void_p),
- ]
-
- ODBC_API.SQLBindParameter.argtypes = [
- ctypes.c_void_p,
- ctypes.c_ushort,
- ctypes.c_short,
- ctypes.c_short,
- ctypes.c_short,
- ctypes.c_size_t,
- ctypes.c_short,
- ctypes.c_void_p,
- ctypes.c_ssize_t,
- ctypes.POINTER(ctypes.c_ssize_t),
- ]
-
- ODBC_API.SQLColAttribute.argtypes = [
- ctypes.c_void_p,
- ctypes.c_ushort,
- ctypes.c_ushort,
- ctypes.c_void_p,
- ctypes.c_short,
- ctypes.POINTER(ctypes.c_short),
- ctypes.POINTER(ctypes.c_ssize_t),
- ]
-
- ODBC_API.SQLDataSources.argtypes = [
- ctypes.c_void_p,
- ctypes.c_ushort,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.POINTER(ctypes.c_short),
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.POINTER(ctypes.c_short),
- ]
-
- ODBC_API.SQLDescribeCol.argtypes = [
- ctypes.c_void_p,
- ctypes.c_ushort,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.POINTER(ctypes.c_short),
- ctypes.POINTER(ctypes.c_short),
- ctypes.POINTER(ctypes.c_size_t),
- ctypes.POINTER(ctypes.c_short),
- ctypes.POINTER(ctypes.c_short),
- ]
-
- ODBC_API.SQLDescribeParam.argtypes = [
- ctypes.c_void_p,
- ctypes.c_ushort,
- ctypes.POINTER(ctypes.c_short),
- ctypes.POINTER(ctypes.c_size_t),
- ctypes.POINTER(ctypes.c_short),
- ctypes.POINTER(ctypes.c_short),
- ]
-
- ODBC_API.SQLDriverConnect.argtypes = [
- ctypes.c_void_p,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.POINTER(ctypes.c_short),
- ctypes.c_ushort,
- ]
-
- ODBC_API.SQLGetData.argtypes = [
- ctypes.c_void_p,
- ctypes.c_ushort,
- ctypes.c_short,
- ctypes.c_void_p,
- ctypes.c_ssize_t,
- ctypes.POINTER(ctypes.c_ssize_t),
- ]
-
- ODBC_API.SQLGetDiagRec.argtypes = [
- ctypes.c_short,
- ctypes.c_void_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.POINTER(ctypes.c_int),
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.POINTER(ctypes.c_short),
- ]
-
- ODBC_API.SQLGetInfo.argtypes = [
- ctypes.c_void_p,
- ctypes.c_ushort,
- ctypes.c_void_p,
- ctypes.c_short,
- ctypes.POINTER(ctypes.c_short),
- ]
-
- ODBC_API.SQLRowCount.argtypes = [
- ctypes.c_void_p,
- ctypes.POINTER(ctypes.c_ssize_t),
- ]
-
- ODBC_API.SQLNumParams.argtypes = [
- ctypes.c_void_p,
- ctypes.POINTER(ctypes.c_short),
- ]
-
- ODBC_API.SQLNumResultCols.argtypes = [
- ctypes.c_void_p,
- ctypes.POINTER(ctypes.c_short),
- ]
-
-
-ODBC_API.SQLCloseCursor.argtypes = [ctypes.c_void_p]
-
-ODBC_API.SQLColumns.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
-]
-
-ODBC_API.SQLConnect.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
-]
-
-
-
-ODBC_API.SQLDisconnect.argtypes = [ctypes.c_void_p]
-
-
-ODBC_API.SQLEndTran.argtypes = [
- ctypes.c_short,
- ctypes.c_void_p,
- ctypes.c_short,
-]
-
-ODBC_API.SQLExecute.argtypes = [ctypes.c_void_p]
-
-ODBC_API.SQLExecDirect.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_int,
-]
-
-ODBC_API.SQLFetch.argtypes = [ctypes.c_void_p]
-
-ODBC_API.SQLFetchScroll.argtypes = [
- ctypes.c_void_p,
- ctypes.c_short,
- ctypes.c_ssize_t,
-]
-
-ODBC_API.SQLForeignKeys.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
-]
-
-ODBC_API.SQLFreeHandle.argtypes = [
- ctypes.c_short,
- ctypes.c_void_p,
-]
-
-ODBC_API.SQLFreeStmt.argtypes = [
- ctypes.c_void_p,
- ctypes.c_ushort,
-]
-
-
-ODBC_API.SQLGetTypeInfo.argtypes = [
- ctypes.c_void_p,
- ctypes.c_short,
-]
-
-ODBC_API.SQLMoreResults.argtypes = [ctypes.c_void_p]
-
-
-ODBC_API.SQLPrepare.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_int,
-]
-
-ODBC_API.SQLPrimaryKeys.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
-]
-
-ODBC_API.SQLProcedureColumns.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
-]
-
-ODBC_API.SQLProcedures.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
-]
-
-
-ODBC_API.SQLSetConnectAttr.argtypes = [
- ctypes.c_void_p,
- ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_int,
-]
-
-ODBC_API.SQLSetEnvAttr.argtypes = [
- ctypes.c_void_p,
- ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_int,
-]
-
-ODBC_API.SQLStatistics.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_ushort,
- ctypes.c_ushort,
-]
-
-ODBC_API.SQLTables.argtypes = [
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
- ctypes.c_char_p,
- ctypes.c_short,
-]
-
-def to_wchar(argtypes):
- if argtypes: # Under IronPython some argtypes are not declared
- result = []
- for x in argtypes:
- if x == ctypes.c_char_p:
- result.append(wchar_type)
- else:
- result.append(x)
- return result
- else:
- return argtypes
-
-ODBC_API.SQLColumnsW.argtypes = to_wchar(ODBC_API.SQLColumns.argtypes)
-ODBC_API.SQLConnectW.argtypes = to_wchar(ODBC_API.SQLConnect.argtypes)
-ODBC_API.SQLDataSourcesW.argtypes = to_wchar(ODBC_API.SQLDataSources.argtypes)
-ODBC_API.SQLDescribeColW.argtypes = to_wchar(ODBC_API.SQLDescribeCol.argtypes)
-ODBC_API.SQLDriverConnectW.argtypes = to_wchar(ODBC_API.SQLDriverConnect.argtypes)
-ODBC_API.SQLExecDirectW.argtypes = to_wchar(ODBC_API.SQLExecDirect.argtypes)
-ODBC_API.SQLForeignKeysW.argtypes = to_wchar(ODBC_API.SQLForeignKeys.argtypes)
-ODBC_API.SQLPrepareW.argtypes = to_wchar(ODBC_API.SQLPrepare.argtypes)
-ODBC_API.SQLPrimaryKeysW.argtypes = to_wchar(ODBC_API.SQLPrimaryKeys.argtypes)
-ODBC_API.SQLProcedureColumnsW.argtypes = to_wchar(ODBC_API.SQLProcedureColumns.argtypes)
-ODBC_API.SQLProceduresW.argtypes = to_wchar(ODBC_API.SQLProcedures.argtypes)
-ODBC_API.SQLStatisticsW.argtypes = to_wchar(ODBC_API.SQLStatistics.argtypes)
-ODBC_API.SQLTablesW.argtypes = to_wchar(ODBC_API.SQLTables.argtypes)
-
-
-# Set the alias for the ctypes functions for beter code readbility or performance.
-ADDR = ctypes.byref
-SQLFetch = ODBC_API.SQLFetch
-SQLExecute = ODBC_API.SQLExecute
-SQLBindParameter = ODBC_API.SQLBindParameter
-
-
-
-
-
-def ctrl_err(ht, h, val_ret):
- """Classify type of ODBC error from (type of handle, handle, return value)
- , and raise with a list"""
- state = create_buffer(5)
- NativeError = ctypes.c_int()
- Message = create_buffer(1024*10)
- Buffer_len = ctypes.c_short()
- err_list = []
- number_errors = 1
-
- while 1:
- ret = ODBC_API.SQLGetDiagRec(ht, h, number_errors, state, \
- NativeError, Message, len(Message), ADDR(Buffer_len))
- if ret == SQL_NO_DATA_FOUND:
- #No more data, I can raise
- if DEBUG: print err_list[0][1]
- state = err_list[0][0]
- err_text = '['+state+'] '+err_list[0][1]
- if state[:2] in ('24','25','42'):
- raise ProgrammingError(state,err_text)
- elif state[:2] in ('22'):
- raise DataError(state,err_text)
- elif state[:2] in ('23') or state == '40002':
- raise IntegrityError(state,err_text)
- elif state == '0A000':
- raise NotSupportedError(state,err_text)
- elif state in ('HYT00','HYT01'):
- raise OperationalError(state,err_text)
- elif state[:2] in ('IM','HY'):
- raise Error(state,err_text)
- else:
- raise DatabaseError(state,err_text)
- break
- elif ret == SQL_INVALID_HANDLE:
- #The handle passed is an invalid handle
- raise ProgrammingError('', 'SQL_INVALID_HANDLE')
- elif ret == SQL_SUCCESS:
- err_list.append((state.value, Message.value, NativeError.value))
- number_errors += 1
-
-
-def validate(ret, handle_type, handle):
- """ Validate return value, if not success, raise exceptions based on the handle """
- if ret not in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA):
- ctrl_err(handle_type, handle, ret)
-
-
-def AllocateEnv():
- if pooling:
- ret = ODBC_API.SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, SQL_CP_ONE_PER_HENV, SQL_IS_UINTEGER)
- validate(ret, SQL_HANDLE_ENV, SQL_NULL_HANDLE)
-
- '''
- Allocate an ODBC environment by initializing the handle shared_env_h
- ODBC enviroment needed to be created, so connections can be created under it
- connections pooling can be shared under one environment
- '''
- global shared_env_h
- shared_env_h = ctypes.c_void_p()
- ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, ADDR(shared_env_h))
- validate(ret, SQL_HANDLE_ENV, shared_env_h)
-
- # Set the ODBC environment's compatibil leve to ODBC 3.0
- ret = ODBC_API.SQLSetEnvAttr(shared_env_h, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0)
- validate(ret, SQL_HANDLE_ENV, shared_env_h)
-
-
-"""
-Here, we have a few callables that determine how a result row is returned.
-
-A new one can be added by creating a callable that:
-- accepts a cursor as its parameter.
-- returns a callable that accepts an iterable containing the row values.
-"""
-
-def TupleRow(cursor):
- """Normal tuple with added attribute `cursor_description`, as in pyodbc.
-
- This is the default.
- """
- class Row(tuple):
- cursor_description = cursor.description
-
- return Row
-
-
-def NamedTupleRow(cursor):
- """Named tuple to allow attribute lookup by name.
-
- Requires py2.6 or above.
- """
- from collections import namedtuple
-
- attr_names = [x[0] for x in cursor._ColBufferList]
-
- class Row(namedtuple('Row', attr_names, rename=True)):
- cursor_description = cursor.description
-
- def __new__(cls, iterable):
- return super(Row, cls).__new__(cls, *iterable)
-
- return Row
-
-
-def MutableNamedTupleRow(cursor):
- """Mutable named tuple to allow attribute to be replaced. This should be
- compatible with pyodbc's Row type.
-
- Requires 3rd-party library "recordtype".
- """
- from recordtype import recordtype
-
- attr_names = [x[0] for x in cursor._ColBufferList]
-
- class Row(recordtype('Row', attr_names, rename=True)):
- cursor_description = cursor.description
-
- def __init__(self, iterable):
- super(Row, self).__init__(*iterable)
-
- def __iter__(self):
- for field_name in self.__slots__:
- yield getattr(self, field_name)
-
- def __getitem__(self, index):
- if isinstance(index, slice):
- return tuple(getattr(self, x) for x in self.__slots__[index])
- return getattr(self, self.__slots__[index])
-
- def __setitem__(self, index, value):
- setattr(self, self.__slots__[index], value)
-
- return Row
-
-
-# The get_type function is used to determine if parameters need to be re-binded
-# against the changed parameter types
-def get_type(v):
- t = type(v)
- if t == str:
- if len(v) >= 255:
- t = 's'
- if t == unicode:
- if len(v) >= 255:
- t = 'u'
- if t == Decimal:
- sv = str(v).replace('-','').strip('0').split('.')
- if len(sv)>1:
- t = (len(sv[0])+len(sv[1]),len(sv[1]))
- else:
- t = (len(sv[0]),0)
- return t
-
-
-
-# The Cursor Class.
-class Cursor:
- def __init__(self, conx, row_type_callable=None):
- """ Initialize self._stmt_h, which is the handle of a statement
- A statement is actually the basis of a python"cursor" object
- """
- self._stmt_h = ctypes.c_void_p()
- self.connection = conx
- self.row_type_callable = row_type_callable or TupleRow
- self.statement = None
- self._last_param_types = None
- self._ParamBufferList = []
- self._ColBufferList = []
- self._row_type = None
- self._buf_cvt_func = []
- self.rowcount = -1
- self.description = None
- self.autocommit = None
- self._ColTypeCodeList = []
- self._outputsize = {}
- self._inputsizers = []
- self.arraysize = 1
- ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_STMT, self.connection.dbc_h, ADDR(self._stmt_h))
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
- self.closed = False
-
-
- def execute(self, query_string, params=None, many_mode=False, call_mode=False):
- """ Execute the query string, with optional parameters.
- If parameters are provided, the query would first be prepared, then executed with parameters;
- If parameters are not provided, only th query sting, it would be executed directly
- """
-
- self._free_results('FREE_STATEMENT')
-
- if params:
- # If parameters exist, first prepare the query then executed with parameters
- if not type(params) in (tuple, list, set):
- raise TypeError("Params must be in a list, tuple, or set")
-
- if not many_mode:
- if query_string != self.statement:
- # if the query is not same as last query, then it is not prepared
- self.prepare(query_string)
-
-
- param_types = map(get_type, params)
-
- if call_mode:
- self._BindParams(param_types, self._pram_io_list)
- else:
- if param_types != self._last_param_types:
- self._BindParams(param_types)
-
-
- # With query prepared, now put parameters into buffers
- col_num = 0
- for param_buffer, param_buffer_len, sql_type in self._ParamBufferList:
- c_char_buf, c_buf_len = '', 0
- param_val = params[col_num]
- if param_val is None:
- c_buf_len = SQL_NULL_DATA
-
- elif type(param_val) == datetime.datetime:
- max_len = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0]
- datetime_str = param_val.strftime('%Y-%m-%d %H:%M:%S.%f')
- c_char_buf = datetime_str[:max_len]
- c_buf_len = len(c_char_buf)
- # print c_buf_len, c_char_buf
-
- elif type(param_val) == datetime.date:
- if self.connection.type_size_dic.has_key(SQL_TYPE_DATE):
- max_len = self.connection.type_size_dic[SQL_TYPE_DATE][0]
- else:
- max_len = 10
- c_char_buf = param_val.isoformat()[:max_len]
- c_buf_len = len(c_char_buf)
- #print c_char_buf
-
- elif type(param_val) == datetime.time:
- if self.connection.type_size_dic.has_key(SQL_TYPE_TIME):
- max_len = self.connection.type_size_dic[SQL_TYPE_TIME][0]
- c_char_buf = param_val.isoformat()[:max_len]
- c_buf_len = len(c_char_buf)
- elif self.connection.type_size_dic.has_key(SQL_SS_TIME2):
- max_len = self.connection.type_size_dic[SQL_SS_TIME2][0]
- c_char_buf = param_val.isoformat()[:max_len]
- c_buf_len = len(c_char_buf)
- else:
- c_buf_len = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0]
- time_str = param_val.isoformat()
- if len(time_str) == 8:
- time_str += '.000'
- c_char_buf = '1900-01-01 '+time_str[0:c_buf_len - 11]
- #print c_buf_len, c_char_buf
-
- elif type(param_val) == bool:
- if param_val == True:
- c_char_buf = '1'
- else:
- c_char_buf = '0'
- c_buf_len = 1
-
- elif type(param_val) in (int, long, float, Decimal):
- c_char_buf = str(param_val)
- c_buf_len = len(c_char_buf)
-
- elif type(param_val) in (str,):
- c_char_buf = param_val
- c_buf_len = len(c_char_buf)
- elif type(param_val) in (unicode,):
- c_char_buf = to_unicode(param_val)
- c_buf_len = len(c_char_buf)
- elif type(param_val) in (bytearray,buffer):
- c_char_buf = str(param_val)
- c_buf_len = len(c_char_buf)
-
- else:
- c_char_buf = param_val
-
-
- if type(param_val) in (bytearray,buffer):
- param_buffer.raw = c_char_buf
-
- else:
- param_buffer.value = c_char_buf
- #print param_buffer, param_buffer.value
-
- if type(param_val) in (unicode,str,'u','s'):
- #ODBC driver will find NUL in unicode and string to determine their length
- param_buffer_len.value = SQL_NTS
- else:
- param_buffer_len.value = c_buf_len
-
- col_num += 1
- ret = SQLExecute(self._stmt_h)
- if ret != SQL_SUCCESS:
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
-
- if not many_mode:
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
-
- else:
- self.execdirect(query_string)
- return (self)
-
-
- def _SQLExecute(self):
- ret = SQLExecute(self._stmt_h)
- if ret != SQL_SUCCESS:
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
-
-
-
- def prepare(self, query_string):
- """prepare a query"""
- if type(query_string) == unicode:
- c_query_string = wchar_type(to_unicode(query_string))
- ret = ODBC_API.SQLPrepareW(self._stmt_h, c_query_string, len(query_string))
- else:
- c_query_string = ctypes.c_char_p(query_string)
- ret = ODBC_API.SQLPrepare(self._stmt_h, c_query_string, len(query_string))
- if ret != SQL_SUCCESS:
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
- self.statement = query_string
-
-
- def execdirect(self, query_string):
- """Execute a query directly"""
- if type(query_string) == unicode:
- c_query_string = wchar_type(to_unicode(query_string))
- ret = ODBC_API.SQLExecDirectW(self._stmt_h, c_query_string, len(query_string))
- else:
- c_query_string = ctypes.c_char_p(query_string)
- ret = ODBC_API.SQLExecDirect(self._stmt_h, c_query_string, len(query_string))
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
- self.statement = None
- return (self)
-
-
- def callproc(self, procname, args):
- raise Warning('', 'Still not fully implemented')
- self._pram_io_list = [row[4] for row in self.procedurecolumns(procedure = procname).fetchall() if row[4] not in (SQL_RESULT_COL, SQL_RETURN_VALUE)]
-
- print 'pram_io_list: '+str(self._pram_io_list)
-
-
-
- call_escape = '{CALL '+procname
- if args:
- call_escape += '(' + ','.join(['?' for params in args]) + ')'
- call_escape += '}'
-
- self.execute(call_escape, args, call_mode = True)
-
- result = []
-
- for buf, buf_len, sql_type in self._ParamBufferList:
- if buf_len.value == -1:
- result.append(None)
- else:
- result.append(self.connection.output_converter[sql_type](buf.value))
- return (result)
-
-
-
- def executemany(self, query_string, params_list = [None]):
- self.prepare(query_string)
- for params in params_list:
- self.execute(query_string, params, many_mode = True)
- self._NumOfRows()
- self.rowcount = -1
- self._UpdateDesc()
- #self._BindCols()
-
-
- def _BindParams(self, param_types, pram_io_list = []):
- """Create parameter buffers based on param types, and bind them to the statement"""
- # Get the number of query parameters judged by database.
- NumParams = ctypes.c_short()
- ret = ODBC_API.SQLNumParams(self._stmt_h, ADDR(NumParams))
- if ret != SQL_SUCCESS:
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- if len(param_types) != NumParams.value:
- # In case number of parameters provided do not same as number required
- error_desc = "The SQL contains %d parameter markers, but %d parameters were supplied" \
- %(NumParams.value,len(param_types))
- raise ProgrammingError('HY000',error_desc)
-
-
- # Every parameter needs to be binded to a buffer
- ParamBufferList = []
- # Temporary holder since we can only call SQLDescribeParam before
- # calling SQLBindParam.
- temp_holder = []
- for col_num in range(NumParams.value):
- col_size = 0
- buf_size = 512
-
- if param_types[col_num] == type(None):
- ParameterNumber = ctypes.c_ushort(col_num + 1)
- DataType = ctypes.c_short()
- ParameterSize = ctypes.c_size_t()
- DecimalDigits = ctypes.c_short()
- Nullable = ctypes.c_short()
- ret = ODBC_API.SQLDescribeParam(
- self._stmt_h,
- ParameterNumber,
- ADDR(DataType),
- ADDR(ParameterSize),
- ADDR(DecimalDigits),
- ADDR(Nullable),
- )
- if ret != SQL_SUCCESS:
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- sql_c_type = SQL_C_DEFAULT
- sql_type = DataType.value
- buf_size = 1
- ParameterBuffer = create_buffer(buf_size)
-
- elif param_types[col_num] in (int,):
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_INTEGER
- buf_size = SQL_data_type_dict[sql_type][4]
- ParameterBuffer = create_buffer(buf_size)
-
- elif param_types[col_num] in (long,):
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_BIGINT
- buf_size = SQL_data_type_dict[sql_type][4]
- ParameterBuffer = create_buffer(buf_size)
-
-
- elif param_types[col_num] == float:
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_DOUBLE
- buf_size = SQL_data_type_dict[sql_type][4]
- ParameterBuffer = create_buffer(buf_size)
-
- elif param_types[col_num] == bool:
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_BIT
- buf_size = SQL_data_type_dict[sql_type][4]
- ParameterBuffer = create_buffer(buf_size)
-
-
- elif type(param_types[col_num]) == tuple: #Decimal
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_NUMERIC
- buf_size = param_types[col_num][0]
-
- ParameterBuffer = create_buffer(buf_size+4)
- col_size = param_types[col_num][1]
- if DEBUG: print param_types[col_num][0],param_types[col_num][1]
-
- elif param_types[col_num] == datetime.datetime:
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_TYPE_TIMESTAMP
- buf_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0]
- ParameterBuffer = create_buffer(buf_size)
- col_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][1]
-
-
- elif param_types[col_num] == datetime.date:
- sql_c_type = SQL_C_CHAR
- if self.connection.type_size_dic.has_key(SQL_TYPE_DATE):
- if DEBUG: print 'conx.type_size_dic.has_key(SQL_TYPE_DATE)'
- sql_type = SQL_TYPE_DATE
- buf_size = self.connection.type_size_dic[SQL_TYPE_DATE][0]
-
- ParameterBuffer = create_buffer(buf_size)
- col_size = self.connection.type_size_dic[SQL_TYPE_DATE][1]
-
- else:
- # SQL Sever <2008 doesn't have a DATE type.
- sql_type = SQL_TYPE_TIMESTAMP
- buf_size = 10
- ParameterBuffer = create_buffer(buf_size)
-
-
- elif param_types[col_num] == datetime.time:
- sql_c_type = SQL_C_CHAR
- if self.connection.type_size_dic.has_key(SQL_TYPE_TIME):
- sql_type = SQL_TYPE_TIME
- buf_size = self.connection.type_size_dic[SQL_TYPE_TIME][0]
- ParameterBuffer = create_buffer(buf_size)
- col_size = self.connection.type_size_dic[SQL_TYPE_TIME][1]
- elif self.connection.type_size_dic.has_key(SQL_SS_TIME2):
- # TIME type added in SQL Server 2008
- sql_type = SQL_SS_TIME2
- buf_size = self.connection.type_size_dic[SQL_SS_TIME2][0]
- ParameterBuffer = create_buffer(buf_size)
- col_size = self.connection.type_size_dic[SQL_SS_TIME2][1]
- else:
- # SQL Sever <2008 doesn't have a TIME type.
- sql_type = SQL_TYPE_TIMESTAMP
- buf_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0]
- ParameterBuffer = create_buffer(buf_size)
- col_size = 3
-
- elif param_types[col_num] == unicode:
- sql_c_type = SQL_C_WCHAR
- sql_type = SQL_WVARCHAR
- buf_size = 255
- ParameterBuffer = create_buffer_u(buf_size)
-
- elif param_types[col_num] == str:
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_VARCHAR
- buf_size = 255
- ParameterBuffer = create_buffer(buf_size)
-
- elif param_types[col_num] == 'u':
- sql_c_type = SQL_C_WCHAR
- sql_type = SQL_WLONGVARCHAR
- buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500
- ParameterBuffer = create_buffer_u(buf_size)
-
- elif param_types[col_num] == 's':
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_LONGVARCHAR
- buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500
- ParameterBuffer = create_buffer(buf_size)
-
-
- elif param_types[col_num] in (bytearray, buffer):
- sql_c_type = SQL_C_BINARY
- sql_type = SQL_LONGVARBINARY
- buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500
- ParameterBuffer = create_buffer(buf_size)
-
-
- else:
- sql_c_type = SQL_C_CHAR
- sql_type = SQL_LONGVARCHAR
- buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500
- ParameterBuffer = create_buffer(buf_size)
-
- temp_holder.append((sql_c_type, sql_type, buf_size, col_size, ParameterBuffer))
-
- for col_num, (sql_c_type, sql_type, buf_size, col_size, ParameterBuffer) in enumerate(temp_holder):
- BufferLen = ctypes.c_ssize_t(buf_size)
- LenOrIndBuf = ctypes.c_ssize_t()
-
-
- InputOutputType = SQL_PARAM_INPUT
- if len(pram_io_list) > col_num:
- InputOutputType = pram_io_list[col_num]
-
- ret = SQLBindParameter(self._stmt_h, col_num + 1, InputOutputType, sql_c_type, sql_type, buf_size,\
- col_size, ADDR(ParameterBuffer), BufferLen,ADDR(LenOrIndBuf))
- if ret != SQL_SUCCESS:
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
- # Append the value buffer and the lenth buffer to the array
- ParamBufferList.append((ParameterBuffer,LenOrIndBuf,sql_type))
-
- self._last_param_types = param_types
- self._ParamBufferList = ParamBufferList
-
-
-
- def _CreateColBuf(self):
- NOC = self._NumOfCols()
- self._ColBufferList = []
- self._row_type = None
- for col_num in range(NOC):
- col_name = self.description[col_num][0]
-
- col_sql_data_type = self._ColTypeCodeList[col_num]
-
- # set default size base on the column's sql data type
- total_buf_len = SQL_data_type_dict[col_sql_data_type][4]
- # over-write if there's preset size value for "large columns"
- if total_buf_len >= 20500:
- total_buf_len = self._outputsize.get(None,total_buf_len)
- # over-write if there's preset size value for the "col_num" column
- total_buf_len = self._outputsize.get(col_num, total_buf_len)
-
-
- alloc_buffer = SQL_data_type_dict[col_sql_data_type][3](total_buf_len)
-
- used_buf_len = ctypes.c_ssize_t()
-
- target_type = SQL_data_type_dict[col_sql_data_type][2]
- force_unicode = self.connection.unicode_results
-
- if force_unicode and col_sql_data_type in (SQL_CHAR,SQL_VARCHAR,SQL_LONGVARCHAR):
- target_type = SQL_C_WCHAR
- alloc_buffer = create_buffer_u(total_buf_len)
-
- buf_cvt_func = self.connection.output_converter[self._ColTypeCodeList[col_num]]
-
- self._ColBufferList.append([col_name, target_type, used_buf_len, alloc_buffer, total_buf_len, buf_cvt_func])
-
-
- def _GetData(self):
- '''Bind buffers for the record set columns'''
-
- # Lazily create the row type on first fetch.
- if self._row_type is None:
- self._row_type = self.row_type_callable(self)
-
- value_list = []
- col_num = 0
- for col_name, target_type, used_buf_len, alloc_buffer, total_buf_len, buf_cvt_func in self._ColBufferList:
-
- blocks = []
- while True:
- ret = ODBC_API.SQLGetData(self._stmt_h, col_num + 1, target_type, ADDR(alloc_buffer), total_buf_len,\
- ADDR(used_buf_len))
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- if ret == SQL_SUCCESS:
- if used_buf_len.value == SQL_NULL_DATA:
- blocks.append(None)
- else:
- if target_type == SQL_C_BINARY:
- blocks.append(alloc_buffer.raw[:used_buf_len.value])
- elif target_type == SQL_C_WCHAR:
- blocks.append(from_buffer_u(alloc_buffer))
- else:
- #print col_name, target_type, alloc_buffer.value
- blocks.append(alloc_buffer.value)
-
- break
-
- if ret == SQL_SUCCESS_WITH_INFO:
- if target_type == SQL_C_BINARY:
- blocks.append(alloc_buffer.raw)
- else:
- blocks.append(alloc_buffer.value)
-
- if ret == SQL_NO_DATA:
- break
-
-
- if len(blocks) == 1:
- raw_value = blocks[0]
- else:
- raw_value = ''.join(blocks)
-
- if raw_value == None:
- value_list.append(None)
- else:
- value_list.append(buf_cvt_func(raw_value))
- col_num += 1
-
- return self._row_type(value_list)
-
-
- def _UpdateDesc(self):
- "Get the information of (name, type_code, display_size, internal_size, col_precision, scale, null_ok)"
- Cname = create_buffer(1024)
- Cname_ptr = ctypes.c_short()
- Ctype_code = ctypes.c_short()
- Csize = ctypes.c_size_t()
- Cdisp_size = ctypes.c_ssize_t(0)
- CDecimalDigits = ctypes.c_short()
- Cnull_ok = ctypes.c_short()
- ColDescr = []
- self._ColTypeCodeList = []
- NOC = self._NumOfCols()
- for col in range(1, NOC+1):
- ret = ODBC_API.SQLColAttribute(self._stmt_h, col, SQL_DESC_DISPLAY_SIZE, ADDR(create_buffer(10)),
- 10, ADDR(ctypes.c_short()),ADDR(Cdisp_size))
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- ret = ODBC_API.SQLDescribeCol(self._stmt_h, col, Cname, len(Cname), ADDR(Cname_ptr),\
- ADDR(Ctype_code),ADDR(Csize),ADDR(CDecimalDigits), ADDR(Cnull_ok))
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- col_name = Cname.value
- if lowercase:
- col_name = str.lower(col_name)
- #(name, type_code, display_size,
- # internal_size, col_precision, scale, null_ok)
- ColDescr.append((col_name, SQL_data_type_dict.get(Ctype_code.value,(Ctype_code.value))[0],Cdisp_size.value,\
- Csize.value, Csize.value,CDecimalDigits.value,Cnull_ok.value == 1 and True or False))
- self._ColTypeCodeList.append(Ctype_code.value)
-
- if len(ColDescr) > 0:
- self.description = ColDescr
- else:
- self.description = None
- self._CreateColBuf()
-
-
- def _NumOfRows(self):
- """Get the number of rows"""
- NOR = ctypes.c_ssize_t()
- ret = ODBC_API.SQLRowCount(self._stmt_h, ADDR(NOR))
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
- self.rowcount = NOR.value
- return self.rowcount
-
-
- def _NumOfCols(self):
- """Get the number of cols"""
- NOC = ctypes.c_short()
- ret = ODBC_API.SQLNumResultCols(self._stmt_h, ADDR(NOC))
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
- return NOC.value
-
-
- def fetchall(self):
- rows = []
- while True:
- row = self.fetchone()
- if row == None:
- break
- rows.append(row)
- return rows
-
-
- def fetchmany(self, num = None):
- if num == None:
- num = self.arraysize
- rows, row_num = [], 0
-
- while row_num < num:
- row = self.fetchone()
- if row == None:
- break
- rows.append(row)
- row_num += 1
- return rows
-
-
- def fetchone(self):
- ret = SQLFetch(self._stmt_h)
- if ret == SQL_SUCCESS:
- return self._GetData()
- else:
- if ret == SQL_NO_DATA_FOUND:
- return None
- else:
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- def next(self):
- row = self.fetchone()
- if row == None:
- raise(StopIteration)
- return row
-
- def __iter__(self):
- return self
-
-
- def skip(self, count = 0):
- for i in xrange(count):
- ret = ODBC_API.SQLFetchScroll(self._stmt_h, SQL_FETCH_NEXT, 0)
- if ret != SQL_SUCCESS:
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
- return None
-
-
-
- def nextset(self):
- ret = ODBC_API.SQLMoreResults(self._stmt_h)
- if ret not in (SQL_SUCCESS, SQL_NO_DATA):
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- if ret == SQL_NO_DATA:
- self._free_results('FREE_STATEMENT')
- return False
- else:
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
- return True
-
-
- def _free_results(self, free_statement):
- if not self.connection.connected:
- raise ProgrammingError('HY000','Attempt to use a closed connection.')
-
- self.description = None
- if free_statement == 'FREE_STATEMENT':
- ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_CLOSE)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
- else:
- ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_UNBIND)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_RESET_PARAMS)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self.rowcount = -1
-
-
-
- def getTypeInfo(self, sqlType = None):
- if sqlType == None:
- type = SQL_ALL_TYPES
- else:
- type = sqlType
- ret = ODBC_API.SQLGetTypeInfo(self._stmt_h, type)
- if ret in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO):
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
- return self.fetchone()
-
-
- def tables(self, table=None, catalog=None, schema=None, tableType=None):
- """Return a list with all tables"""
- l_catalog = l_schema = l_table = l_tableType = 0
-
- if catalog != None:
- l_catalog = len(catalog)
- catalog = ctypes.c_char_p(catalog)
-
- if schema != None:
- l_schema = len(schema)
- schema = ctypes.c_char_p(schema)
-
- if table != None:
- l_table = len(table)
- table = ctypes.c_char_p(table)
-
- if tableType != None:
- l_tableType = len(tableType)
- tableType = ctypes.c_char_p(tableType)
-
- self._free_results('FREE_STATEMENT')
- self.statement = None
- ret = ODBC_API.SQLTables(self._stmt_h,
- catalog, l_catalog,
- schema, l_schema,
- table, l_table,
- tableType, l_tableType)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
- return (self)
-
-
- def columns(self, table=None, catalog=None, schema=None, column=None):
- """Return a list with all columns"""
- l_catalog = l_schema = l_table = l_column = 0
- if catalog != None:
- l_catalog = len(catalog)
- catalog = ctypes.c_char_p(catalog)
- if schema != None:
- l_schema = len(schema)
- schema = ctypes.c_char_p(schema)
- if table != None:
- l_table = len(table)
- table = ctypes.c_char_p(table)
- if column != None:
- l_column = len(column)
- column = ctypes.c_char_p(column)
-
- self._free_results('FREE_STATEMENT')
- self.statement = None
-
- ret = ODBC_API.SQLColumns(self._stmt_h,
- catalog, l_catalog,
- schema, l_schema,
- table, l_table,
- column, l_column)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
- return (self)
-
-
- def primaryKeys(self, table=None, catalog=None, schema=None):
- l_catalog = l_schema = l_table = 0
- if catalog != None:
- l_catalog = len(catalog)
- catalog = ctypes.c_char_p(catalog)
-
- if schema != None:
- l_schema = len(schema)
- schema = ctypes.c_char_p(schema)
-
- if table != None:
- l_table = len(table)
- table = ctypes.c_char_p(table)
-
- self._free_results('FREE_STATEMENT')
- self.statement = None
-
- ret = ODBC_API.SQLPrimaryKeys(self._stmt_h,
- catalog, l_catalog,
- schema, l_schema,
- table, l_table)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
- return (self)
-
-
- def foreignKeys(self, table=None, catalog=None, schema=None, foreignTable=None, foreignCatalog=None, foreignSchema=None):
- l_catalog = l_schema = l_table = l_foreignTable = l_foreignCatalog = l_foreignSchema = 0
- if catalog != None:
- l_catalog = len(catalog)
- catalog = ctypes.c_char_p(catalog)
- if schema != None:
- l_schema = len(schema)
- schema = ctypes.c_char_p(schema)
- if table != None:
- l_table = len(table)
- table = ctypes.c_char_p(table)
- if foreignTable != None:
- l_foreignTable = len(foreignTable)
- foreignTable = ctypes.c_char_p(foreignTable)
- if foreignCatalog != None:
- l_foreignCatalog = len(foreignCatalog)
- foreignCatalog = ctypes.c_char_p(foreignCatalog)
- if foreignSchema != None:
- l_foreignSchema = len(foreignSchema)
- foreignSchema = ctypes.c_char_p(foreignSchema)
-
- self._free_results('FREE_STATEMENT')
- self.statement = None
-
- ret = ODBC_API.SQLForeignKeys(self._stmt_h,
- catalog, l_catalog,
- schema, l_schema,
- table, l_table,
- foreignCatalog, l_foreignCatalog,
- foreignSchema, l_foreignSchema,
- foreignTable, l_foreignTable)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
- return (self)
-
-
- def procedurecolumns(self, procedure=None, catalog=None, schema=None, column=None):
- l_catalog = l_schema = l_procedure = l_column = 0
- if catalog != None:
- l_catalog = len(catalog)
- catalog = ctypes.c_char_p(catalog)
- if schema != None:
- l_schema = len(schema)
- schema = ctypes.c_char_p(schema)
- if procedure != None:
- l_procedure = len(procedure)
- procedure = ctypes.c_char_p(procedure)
- if column != None:
- l_column = len(column)
- column = ctypes.c_char_p(column)
-
-
- self._free_results('FREE_STATEMENT')
- self.statement = None
-
- ret = ODBC_API.SQLProcedureColumns(self._stmt_h,
- catalog, l_catalog,
- schema, l_schema,
- procedure, l_procedure,
- column, l_column)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self._NumOfRows()
- self._UpdateDesc()
- return (self)
-
-
- def procedures(self, procedure=None, catalog=None, schema=None):
- l_catalog = l_schema = l_procedure = 0
- if catalog != None:
- l_catalog = len(catalog)
- catalog = ctypes.c_char_p(catalog)
- if schema != None:
- l_schema = len(schema)
- schema = ctypes.c_char_p(schema)
- if procedure != None:
- l_procedure = len(procedure)
- procedure = ctypes.c_char_p(procedure)
-
-
- self._free_results('FREE_STATEMENT')
- self.statement = None
-
- ret = ODBC_API.SQLProcedures(self._stmt_h,
- catalog, l_catalog,
- schema, l_schema,
- procedure, l_procedure)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self._NumOfRows()
- self._UpdateDesc()
- return (self)
-
-
- def statistics(self, table, catalog=None, schema=None, unique=False, quick=True):
- l_table = l_catalog = l_schema = 0
-
- if catalog != None:
- l_catalog = len(catalog)
- catalog = ctypes.c_char_p(catalog)
- if schema != None:
- l_schema = len(schema)
- schema = ctypes.c_char_p(schema)
- if table != None:
- l_table = len(table)
- table = ctypes.c_char_p(table)
-
- if unique:
- Unique = SQL_INDEX_UNIQUE
- else:
- Unique = SQL_INDEX_ALL
- if quick:
- Reserved = SQL_QUICK
- else:
- Reserved = SQL_ENSURE
-
- self._free_results('FREE_STATEMENT')
- self.statement = None
-
- ret = ODBC_API.SQLStatistics(self._stmt_h,
- catalog, l_catalog,
- schema, l_schema,
- table, l_table,
- Unique, Reserved)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self._NumOfRows()
- self._UpdateDesc()
- #self._BindCols()
- return (self)
-
-
- def commit(self):
- self.connection.commit()
-
- def rollback(self):
- self.connection.rollback()
-
- def setoutputsize(self, size, column = None):
- self._outputsize[column] = size
-
- def setinputsizes(self, sizes):
- self._inputsizers = [size for size in sizes]
-
-
- def close(self):
- """ Call SQLCloseCursor API to free the statement handle"""
-# ret = ODBC_API.SQLCloseCursor(self._stmt_h)
-# validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-#
- ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_CLOSE)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_UNBIND)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_RESET_PARAMS)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_STMT, self._stmt_h)
- validate(ret, SQL_HANDLE_STMT, self._stmt_h)
-
- self.closed = True
-
-
-
- def __del__(self):
- if not self.closed:
- if DEBUG: print 'auto closing cursor: ',
- try:
- self.close()
- except:
- if DEBUG: print 'failed'
- pass
- else:
- if DEBUG: print 'succeed'
- pass
-
- def __exit__(self, type, value, traceback):
- if value:
- self.rollback()
- else:
- self.commit()
-
- self.close()
-
-
- def __enter__(self):
- return self
-
-
-# This class implement a odbc connection.
-#
-#
-
-class Connection:
- def __init__(self, connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False, **kargs):
- """Init variables and connect to the engine"""
- self.connected = 0
- self.type_size_dic = {}
- self.unicode_results = False
- self.dbc_h = ctypes.c_void_p()
- self.autocommit = autocommit
- self.readonly = False
- self.timeout = 0
-
- for key, value in kargs.items():
- connectString = connectString + key + '=' + value + ';'
- self.connectString = connectString
-
-
- self.clear_output_converters()
-
- with lock:
- if shared_env_h == None:
- #Initialize an enviroment if it is not created.
- AllocateEnv()
-
- # Allocate an DBC handle self.dbc_h under the environment shared_env_h
- # This DBC handle is actually the basis of a "connection"
- # The handle of self.dbc_h will be used to connect to a certain source
- # in the self.connect and self.ConnectByDSN method
-
- ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_DBC, shared_env_h, ADDR(self.dbc_h))
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-
- self.connect(connectString, autocommit, ansi, timeout, unicode_results, readonly)
-
-
-
- def connect(self, connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False):
- """Connect to odbc, using connect strings and set the connection's attributes like autocommit and timeout
- by calling SQLSetConnectAttr
- """
-
- # Before we establish the connection by the connection string
- # Set the connection's attribute of "timeout" (Actully LOGIN_TIMEOUT)
- if timeout != 0:
- ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_LOGIN_TIMEOUT, timeout, SQL_IS_UINTEGER);
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-
-
- # Create one connection with a connect string by calling SQLDriverConnect
- # and make self.dbc_h the handle of this connection
-
-
- # Convert the connetsytring to encoded string
- # so it can be converted to a ctypes c_char array object
-
-
-
- if not ansi:
- c_connectString = wchar_type(to_unicode(self.connectString))
- odbc_func = ODBC_API.SQLDriverConnectW
- else:
- c_connectString = ctypes.c_char_p(self.connectString)
- odbc_func = ODBC_API.SQLDriverConnect
-
- # With unixODBC, SQLDriverConnect will intermittently fail with error:
- # [01000] [unixODBC][Driver Manager]Can't open lib '/path/to/so' : file not found"
- # or:
- # [01000] [unixODBC][Driver Manager]Can't open lib '/path/to/so' : (null)"
- # when called concurrently by more than one threads. So, we have to
- # use a lock to serialize the calls. By the way, the error is much
- # less likely to happen if ODBC Tracing is enabled, likely due to the
- # implicit serialization caused by writing to trace file.
- if ODBC_API._name != 'odbc32':
- with lock:
- ret = odbc_func(self.dbc_h, 0, c_connectString, len(self.connectString), None, 0, None, SQL_DRIVER_NOPROMPT)
- else:
- ret = odbc_func(self.dbc_h, 0, c_connectString, len(self.connectString), None, 0, None, SQL_DRIVER_NOPROMPT)
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-
-
- # Set the connection's attribute of "autocommit"
- #
- self.autocommit = autocommit
-
- if self.autocommit == True:
- ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER)
- else:
- ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER)
-
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-
- # Set the connection's attribute of "readonly"
- #
- self.readonly = readonly
-
- ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_ACCESS_MODE, self.readonly and SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE, SQL_IS_UINTEGER)
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-
- self.unicode_results = unicode_results
- self.update_type_size_info()
- self.connected = 1
-
- def clear_output_converters(self):
- self.output_converter = {}
- for sqltype, profile in SQL_data_type_dict.items():
- self.output_converter[sqltype] = profile[1]
-
-
- def add_output_converter(self, sqltype, func):
- self.output_converter[sqltype] = func
-
- def settimeout(self, timeout):
- ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_CONNECTION_TIMEOUT, timeout, SQL_IS_UINTEGER);
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
- self.timeout = timeout
-
-
- def ConnectByDSN(self, dsn, user, passwd = ''):
- """Connect to odbc, we need dsn, user and optionally password"""
- self.dsn = dsn
- self.user = user
- self.passwd = passwd
-
- sn = create_buffer(dsn)
- un = create_buffer(user)
- pw = create_buffer(passwd)
-
- ret = ODBC_API.SQLConnect(self.dbc_h, sn, len(sn), un, len(un), pw, len(pw))
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-
- self.update_type_size_info()
- self.connected = 1
-
-
- def cursor(self, row_type_callable=None):
- #self.settimeout(self.timeout)
- if not self.connected:
- raise ProgrammingError('HY000','Attempt to use a closed connection.')
-
-
- return Cursor(self, row_type_callable=row_type_callable)
-
- def update_type_size_info(self):
- for sql_type in (
- SQL_TYPE_TIMESTAMP,
- SQL_TYPE_DATE,
- SQL_TYPE_TIME,
- SQL_SS_TIME2,
- ):
- cur = Cursor(self)
- info_tuple = cur.getTypeInfo(sql_type)
- if info_tuple != None:
- self.type_size_dic[sql_type] = info_tuple[2], info_tuple[14]
- cur.close()
-
-
- def commit(self):
- if not self.connected:
- raise ProgrammingError('HY000','Attempt to use a closed connection.')
-
- ret = ODBC_API.SQLEndTran(SQL_HANDLE_DBC, self.dbc_h, SQL_COMMIT);
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-
- def rollback(self):
- if not self.connected:
- raise ProgrammingError('HY000','Attempt to use a closed connection.')
-
- ret = ODBC_API.SQLEndTran(SQL_HANDLE_DBC, self.dbc_h, SQL_ROLLBACK);
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-
-
-
- def getinfo(self,infotype):
- if infotype not in aInfoTypes.keys():
- raise ProgrammingError('HY000','Invalid getinfo value: '+str(infotype))
-
-
- if aInfoTypes[infotype] == 'GI_UINTEGER':
- total_buf_len = 1000
- alloc_buffer = ctypes.c_ulong()
- used_buf_len = ctypes.c_short()
- ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\
- ADDR(used_buf_len))
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
- result = alloc_buffer.value
-
- elif aInfoTypes[infotype] == 'GI_USMALLINT':
- total_buf_len = 1000
- alloc_buffer = ctypes.c_ushort()
- used_buf_len = ctypes.c_short()
- ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\
- ADDR(used_buf_len))
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
- result = alloc_buffer.value
-
- else:
- total_buf_len = 1000
- alloc_buffer = create_buffer(total_buf_len)
- used_buf_len = ctypes.c_short()
- ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\
- ADDR(used_buf_len))
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
- result = alloc_buffer.value
- if aInfoTypes[infotype] == 'GI_YESNO':
- if result[0] == 'Y':
- result = True
- else:
- result = False
-
- return result
-
- def __exit__(self, type, value, traceback):
- if value:
- self.rollback()
- else:
- self.commit()
-
- if self.connected:
- self.close()
-
- def __enter__(self):
- return self
-
- def __del__(self):
- if self.connected:
- self.close()
-
- def close(self):
- if not self.connected:
- raise ProgrammingError('HY000','Attempt to close a closed connection.')
-
-
- if self.connected:
- if DEBUG: print 'disconnect'
- if not self.autocommit:
- self.rollback()
- ret = ODBC_API.SQLDisconnect(self.dbc_h)
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
- if DEBUG: print 'free dbc'
- ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_DBC, self.dbc_h)
- validate(ret, SQL_HANDLE_DBC, self.dbc_h)
-# if shared_env_h.value:
-# if DEBUG: print 'env'
-# ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_ENV, shared_env_h)
-# validate(ret, SQL_HANDLE_ENV, shared_env_h)
- self.connected = 0
-
-odbc = Connection
-connect = odbc
-'''
-def connect(connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False, **kargs):
- return odbc(connectString, autocommit, ansi, timeout, unicode_results, readonly, kargs)
-'''
-
-def win_create_mdb(mdb_path, sort_order = "General\0\0"):
- #CREATE_DB=
- ctypes.windll.ODBCCP32.SQLConfigDataSource.argtypes = [ctypes.c_void_p,ctypes.c_ushort,ctypes.c_char_p,ctypes.c_char_p]
- c_Path = "CREATE_DB=" + mdb_path + " " + sort_order
- ODBC_ADD_SYS_DSN = 1
- ret = ctypes.windll.ODBCCP32.SQLConfigDataSource(None,ODBC_ADD_SYS_DSN,"Microsoft Access Driver (*.mdb)", c_Path)
- if not ret:
- raise Exception('Failed to create Access mdb file. Please check file path, permission and Access driver readiness.')
-
-
-def win_compact_mdb(mdb_path, compacted_mdb_path, sort_order = "General\0\0"):
- #COMPACT_DB=
- c_Path = "COMPACT_DB=" + mdb_path + " " + compacted_mdb_path + " " + sort_order
- ODBC_ADD_SYS_DSN = 1
- ctypes.windll.ODBCCP32.SQLConfigDataSource.argtypes = [ctypes.c_void_p,ctypes.c_ushort,ctypes.c_char_p,ctypes.c_char_p]
- ret = ctypes.windll.ODBCCP32.SQLConfigDataSource(None,ODBC_ADD_SYS_DSN,"Microsoft Access Driver (*.mdb)", c_Path)
- if not ret:
- raise Exception('Failed to compact Access mdb file. Please check file path, permission and Access driver readiness.')
-
-
-def dataSources():
- """Return a list with [name, descrition]"""
- dsn = create_buffer(1024)
- desc = create_buffer(1024)
- dsn_len = ctypes.c_short()
- desc_len = ctypes.c_short()
- dsn_list = {}
- with lock:
- if shared_env_h == None:
- AllocateEnv()
- while 1:
- ret = ODBC_API.SQLDataSources(shared_env_h, SQL_FETCH_NEXT, \
- dsn, len(dsn), ADDR(dsn_len), desc, len(desc), ADDR(desc_len))
- if ret == SQL_NO_DATA_FOUND:
- break
- elif not ret in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO):
- ctrl_err(SQL_HANDLE_ENV, shared_env_h, ret)
- else:
- dsn_list[dsn.value] = desc.value
- return dsn_list
From a7c6268d2c9f238e0da3a9c0ffdbe319e02ddb0f Mon Sep 17 00:00:00 2001
From: Massimo
Date: Tue, 5 Feb 2013 13:23:51 -0600
Subject: [PATCH 31/39] unlocking session in download
---
VERSION | 2 +-
gluon/globals.py | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index b6c2fb80..3542a5a0 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.05.10.43.24
+Version 2.4.1-alpha.2+timestamp.2013.02.05.13.23.12
diff --git a/gluon/globals.py b/gluon/globals.py
index 09397aa5..be64eb26 100644
--- a/gluon/globals.py
+++ b/gluon/globals.py
@@ -385,6 +385,8 @@ class Response(Storage):
downloads from http://..../download/filename
"""
+ current.session.forget(current.response)
+
if not request.args:
raise HTTP(404)
name = request.args[-1]
@@ -689,7 +691,7 @@ class Session(Storage):
def forget(self, response=None):
self._close(response)
- self._forget = True
+ self._forget = True
def _try_store_in_cookie(self, request, response):
if response.session_storage_type != 'cookie':
From d1d3c171fd1e0d19118e7039b61ab6d1f53eac32 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 6 Feb 2013 09:53:52 -0600
Subject: [PATCH 32/39] fixed issue 1321, shorter admin titles, thanks Argetlam
Akshet
---
VERSION | 2 +-
applications/admin/controllers/default.py | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/VERSION b/VERSION
index 3542a5a0..981c472f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.05.13.23.12
+Version 2.4.1-alpha.2+timestamp.2013.02.06.09.53.07
diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py
index 019b25d0..618b9ff6 100644
--- a/applications/admin/controllers/default.py
+++ b/applications/admin/controllers/default.py
@@ -565,6 +565,7 @@ def edit():
# Load json only if it is ajax edited...
app = get_app(request.vars.app)
filename = '/'.join(request.args)
+ response.title = request.args[-1]
if request.vars.app:
path = abspath(filename)
else:
@@ -812,6 +813,7 @@ def edit_language():
""" Edit language file """
app = get_app()
filename = '/'.join(request.args)
+ response.title = request.args[-1]
strings = read_dict(apath(filename, r=request))
if '__corrupted__' in strings:
From 9afa21cd30db59f1534d6ddfec95082a3e7bcf88 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 6 Feb 2013 10:12:13 -0600
Subject: [PATCH 33/39] simpler Makefile
---
Makefile | 8 +++-----
VERSION | 2 +-
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/Makefile b/Makefile
index f38e1bc3..fb5ed48d 100644
--- a/Makefile
+++ b/Makefile
@@ -61,13 +61,12 @@ mdp:
make app
make win
app:
- echo 'did you uncomment import_all in gluon/main.py?'
- python2.5 -c 'import compileall; compileall.compile_dir("gluon/")'
+ python2.7 -c 'import compileall; compileall.compile_dir("gluon/")'
#python web2py.py -S welcome -R __exit__.py
#cd ../web2py_osx/site-packages/; unzip ../site-packages.zip
find gluon -path '*.pyc' -exec cp {} ../web2py_osx/site-packages/{} \;
cd ../web2py_osx/site-packages/; zip -r ../site-packages.zip *
- mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.5
+ mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.7
cp README.markdown ../web2py_osx/web2py/web2py.app/Contents/Resources
cp NEWINSTALL ../web2py_osx/web2py/web2py.app/Contents/Resources
cp LICENSE ../web2py_osx/web2py/web2py.app/Contents/Resources
@@ -86,7 +85,6 @@ app:
cd ../web2py_osx; zip -r web2py_osx.zip web2py
mv ../web2py_osx/web2py_osx.zip .
win:
- echo 'did you uncomment import_all in gluon/main.py?'
python2.7 -c 'import compileall; compileall.compile_dir("gluon/")'
#cd ../web2py_win/library/; unzip ../library.zip
find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \;
@@ -110,7 +108,7 @@ win:
cd ../web2py_win; zip -r web2py_win.zip web2py
mv ../web2py_win/web2py_win.zip .
run:
- python2.5 web2py.py -a hello
+ python2.7 web2py.py -a hello
commit:
python web2py.py --run_system_tests
make src
diff --git a/VERSION b/VERSION
index 981c472f..63a0ad56 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.06.09.53.07
+Version 2.4.1-alpha.2+timestamp.2013.02.06.10.11.34
From 4a8a41de91c94bb4c397c7f96e80f7854cd73b1b Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 6 Feb 2013 10:21:41 -0600
Subject: [PATCH 34/39] if False: import import_all
---
VERSION | 2 +-
gluon/main.py | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/VERSION b/VERSION
index 63a0ad56..7f90bff1 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.06.10.11.34
+Version 2.4.1-alpha.2+timestamp.2013.02.06.10.21.01
diff --git a/gluon/main.py b/gluon/main.py
index 3aee0956..5d1c1081 100644
--- a/gluon/main.py
+++ b/gluon/main.py
@@ -12,6 +12,7 @@ Contains:
"""
+if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
import gc
import cgi
import cStringIO
From 0c0830c1dfe3c61d55e9201a5f0c0c66f0eb3606 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 6 Feb 2013 10:32:02 -0600
Subject: [PATCH 35/39] added missing files to git
---
Makefile | 2 +-
VERSION | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index fb5ed48d..77d99f49 100644
--- a/Makefile
+++ b/Makefile
@@ -54,7 +54,7 @@ src:
### build web2py_src.zip
echo '' > NEWINSTALL
mv web2py_src.zip web2py_src_old.zip | echo 'no old'
- cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/splashlogo.gif web2py/*.py web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/Makefile web2py/epydoc.css web2py/epydoc.conf web2py/app.example.yaml web2py/logging.example.conf web2py_exe.conf web2py/queue.example.yaml MANIFEST.in w2p_apps w2p_clone w2p_run startweb2py web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
+ cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/splashlogo.gif web2py/*.py web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/Makefile web2py/epydoc.css web2py/epydoc.conf web2py/app.example.yaml web2py/logging.example.conf web2py/queue.example.yaml MANIFEST.in w2p_apps w2p_clone w2p_run web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
mdp:
make src
diff --git a/VERSION b/VERSION
index 7f90bff1..ea981eb9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.06.10.21.01
+Version 2.4.1-alpha.2+timestamp.2013.02.06.10.31.19
From 5ab85ee20e3bb96b976ff8511bf51bf97d111090 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 6 Feb 2013 11:34:42 -0600
Subject: [PATCH 36/39] fixed importing of local packages first (I hope)
---
VERSION | 2 +-
gluon/custom_import.py | 36 ++++++++++++++++++++----------------
2 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/VERSION b/VERSION
index ea981eb9..5d6f6bbe 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.06.10.31.19
+Version 2.4.1-alpha.2+timestamp.2013.02.06.11.34.01
diff --git a/gluon/custom_import.py b/gluon/custom_import.py
index 0233e331..0d62c37f 100644
--- a/gluon/custom_import.py
+++ b/gluon/custom_import.py
@@ -61,22 +61,26 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
and isinstance(globals, dict):
import_tb = None
try:
- items = current.request.folder.split(os.path.sep)
- if not items[-1]:
- items = items[:-1]
- modules_prefix = '.'.join(items[-2:]) + '.modules'
- if not fromlist:
- # import like "import x" or "import x.y"
- result = None
- for itemname in name.split("."):
- new_mod = base_importer(
- modules_prefix, globals, locals, [itemname], level)
- try:
- result = result or new_mod.__dict__[itemname]
- except KeyError, e:
- raise ImportError, 'Cannot import module %s' % str(e)
- modules_prefix += "." + itemname
- return result
+ try:
+ oname = name if not name.startswith('.') else '.'+name
+ return NATIVE_IMPORTER(oname, globals, locals, fromlist, level)
+ except ImportError:
+ items = current.request.folder.split(os.path.sep)
+ if not items[-1]:
+ items = items[:-1]
+ modules_prefix = '.'.join(items[-2:]) + '.modules'
+ if not fromlist:
+ # import like "import x" or "import x.y"
+ result = None
+ for itemname in name.split("."):
+ new_mod = base_importer(
+ modules_prefix, globals, locals, [itemname], level)
+ try:
+ result = result or new_mod.__dict__[itemname]
+ except KeyError, e:
+ raise ImportError, 'Cannot import module %s' % str(e)
+ modules_prefix += "." + itemname
+ return result
else:
# import like "from x import a, b, ..."
pname = modules_prefix + "." + name
From f90dbb9321e0b34ebd1f2da54c60c8738005e539 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Wed, 6 Feb 2013 17:22:11 -0600
Subject: [PATCH 37/39] fixed custom_import indentation
---
VERSION | 2 +-
gluon/custom_import.py | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/VERSION b/VERSION
index 5d6f6bbe..3642bea8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.06.11.34.01
+Version 2.4.1-alpha.2+timestamp.2013.02.06.17.21.25
diff --git a/gluon/custom_import.py b/gluon/custom_import.py
index 0d62c37f..b1c67e0c 100644
--- a/gluon/custom_import.py
+++ b/gluon/custom_import.py
@@ -81,10 +81,10 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
raise ImportError, 'Cannot import module %s' % str(e)
modules_prefix += "." + itemname
return result
- else:
- # import like "from x import a, b, ..."
- pname = modules_prefix + "." + name
- return base_importer(pname, globals, locals, fromlist, level)
+ else:
+ # import like "from x import a, b, ..."
+ pname = modules_prefix + "." + name
+ return base_importer(pname, globals, locals, fromlist, level)
except ImportError, e1:
import_tb = sys.exc_info()[2]
try:
From 343f295b6bfab33cf2fe62238a97ea1388913ec6 Mon Sep 17 00:00:00 2001
From: mdipierro
Date: Thu, 7 Feb 2013 05:37:14 -0600
Subject: [PATCH 38/39] better parse as rest, thanks Denes
---
VERSION | 2 +-
gluon/dal.py | 13 +++++++++----
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/VERSION b/VERSION
index 3642bea8..56314150 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.06.17.21.25
+Version 2.4.1-alpha.2+timestamp.2013.02.07.05.36.19
diff --git a/gluon/dal.py b/gluon/dal.py
index 839e3704..f8711f81 100644
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -7394,10 +7394,12 @@ def index():
return Row({'status':200,'pattern':'list',
'error':None,'response':patterns})
for pattern in patterns:
+ basequery, exposedfields = None, []
if isinstance(pattern,tuple):
- pattern, basequery = pattern
- else:
- basequery = None
+ if len(pattern)==2:
+ pattern, basequery = pattern
+ elif len(pattern)>2:
+ pattern, basequery, exposedfields = pattern[0:3]
otable=table=None
if not isinstance(queries,dict):
dbset=db(queries)
@@ -7510,7 +7512,10 @@ def index():
orderby = [db[table][f] if not f.startswith('~') else ~db[table][f[1:]] for f in ofields]
except (KeyError, AttributeError):
return Row({'status':400,'error':'invalid orderby','response':None})
- fields = [field for field in db[table] if field.readable]
+ if exposedfields:
+ fields = [field for field in db[table] if str(field).split('.')[-1] in exposedfields and field.readable]
+ else:
+ fields = [field for field in db[table] if field.readable]
count = dbset.count()
try:
offset = int(vars.get('offset',None) or 0)
From 66c3855e23bf72bf8d4cacdeb9219a47ae5821b8 Mon Sep 17 00:00:00 2001
From: Massimo
Date: Fri, 8 Feb 2013 12:42:10 -0600
Subject: [PATCH 39/39] id!=None in grid
---
VERSION | 2 +-
gluon/sqlhtml.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index 56314150..c986fead 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Version 2.4.1-alpha.2+timestamp.2013.02.07.05.36.19
+Version 2.4.1-alpha.2+timestamp.2013.02.08.12.41.08
diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py
index 750df107..b3e61b91 100644
--- a/gluon/sqlhtml.py
+++ b/gluon/sqlhtml.py
@@ -2514,7 +2514,7 @@ class SQLFORM(FORM):
except (KeyError, ValueError, TypeError):
redirect(URL(args=table._tablename))
if nargs == len(args) + 1:
- query = table._id > 0
+ query = table._id != None
# filter out data info for displayed table
if table._tablename in constraints: