Author: phd
Date: Sat Oct 29 07:37:21 2011
New Revision: 4465
Log:
Quoting rules changed for PostgreSQL: SQLObject uses E'' escape string
if the string contains characters escaped with backslash.
Modified:
SQLObject/trunk/docs/News.txt
SQLObject/trunk/sqlobject/converters.py
SQLObject/trunk/sqlobject/sqlbuilder.py
SQLObject/trunk/sqlobject/tests/test_converters.py
Modified: SQLObject/trunk/docs/News.txt
==============================================================================
--- SQLObject/trunk/docs/News.txt Tue Oct 11 11:10:18 2011 (r4464)
+++ SQLObject/trunk/docs/News.txt Sat Oct 29 07:37:21 2011 (r4465)
@@ -23,6 +23,12 @@
* delColumn now accepts a ForeignKey's name without 'ID'.
+* Support for PostgreSQL 7.* is dropped. The minimal supported version of
+ PostgreSQL is 8.1 now.
+
+* Quoting rules changed for PostgreSQL: SQLObject uses E'' escape string
+ if the string contains characters escaped with backslash.
+
* A bug caused by psycopg2 recently added a new boolean not callable
autocommit attribute was fixed.
Modified: SQLObject/trunk/sqlobject/converters.py
==============================================================================
--- SQLObject/trunk/sqlobject/converters.py Tue Oct 11 11:10:18 2011 (r4464)
+++ SQLObject/trunk/sqlobject/converters.py Sat Oct 29 07:37:21 2011 (r4465)
@@ -1,5 +1,10 @@
-import sys
from array import array
+import datetime
+from decimal import Decimal
+import sys
+import time
+from types import ClassType, InstanceType, NoneType
+
try:
import mx.DateTime.ISO
@@ -15,17 +20,12 @@
DateTimeType = None
DateTimeDeltaType = None
-import time
-import datetime
-
try:
import Sybase
NumericType=Sybase.NumericType
except ImportError:
NumericType = None
-from decimal import Decimal
-from types import ClassType, InstanceType, NoneType
########################################
## Quoting
@@ -90,6 +90,8 @@
value = value.replace("'", "''")
else:
assert 0, "Database %s unknown" % db
+ if db in ('postgres', 'rdbhost') and ('\\' in value):
+ return "E'%s'" % value
return "'%s'" % value
registerConverter(str, StringLikeConverter)
@@ -188,12 +190,12 @@
registerConverter(Decimal, DecimalConverter)
def TimedeltaConverter(value, db):
-
+
return """INTERVAL '%d days %d seconds'""" % \
(value.days, value.seconds)
registerConverter(datetime.timedelta, TimedeltaConverter)
-
+
def sqlrepr(obj, db=None):
try:
@@ -206,3 +208,17 @@
return converter(obj, db)
else:
return reprFunc(db)
+
+
+def quote_str(s, db):
+ if db in ('postgres', 'rdbhost') and ('\\' in s):
+ return "E'%s'" % s
+ return "'%s'" % s
+
+def unquote_str(s):
+ if s.upper().startswith("E'") and s.endswith("'"):
+ return s[2:-1]
+ elif s.startswith("'") and s.endswith("'"):
+ return s[1:-1]
+ else:
+ return s
Modified: SQLObject/trunk/sqlobject/sqlbuilder.py
==============================================================================
--- SQLObject/trunk/sqlobject/sqlbuilder.py Tue Oct 11 11:10:18 2011 (r4464)
+++ SQLObject/trunk/sqlobject/sqlbuilder.py Sat Oct 29 07:37:21 2011 (r4465)
@@ -68,7 +68,7 @@
import weakref
import classregistry
-from converters import sqlrepr, registerConverter
+from converters import registerConverter, sqlrepr, quote_str, unquote_str
class VersionError(Exception):
@@ -904,18 +904,18 @@
if isinstance(s, SQLExpression):
values = []
if self.prefix:
- values.append("'%s'" % self.prefix)
+ values.append(quote_str(self.prefix, db))
s = _quote_like_special(sqlrepr(s, db), db)
values.append(s)
if self.postfix:
- values.append("'%s'" % self.postfix)
+ values.append(quote_str(self.postfix, db))
if db == "mysql":
return "CONCAT(%s)" % ", ".join(values)
else:
return " || ".join(values)
elif isinstance(s, basestring):
- s = _quote_like_special(sqlrepr(s, db)[1:-1], db)
- return "'%s%s%s'" % (self.prefix, s, self.postfix)
+ s = _quote_like_special(unquote_str(sqlrepr(s, db)), db)
+ return quote_str("%s%s%s" % (self.prefix, s, self.postfix), db)
else:
raise TypeError, "expected str, unicode or SQLExpression, got %s" % type(s)
Modified: SQLObject/trunk/sqlobject/tests/test_converters.py
==============================================================================
--- SQLObject/trunk/sqlobject/tests/test_converters.py Tue Oct 11 11:10:18 2011 (r4464)
+++ SQLObject/trunk/sqlobject/tests/test_converters.py Sat Oct 29 07:37:21 2011 (r4465)
@@ -1,10 +1,12 @@
-import sys
from datetime import timedelta
-from sqlobject.sqlbuilder import sqlrepr
+import sys
+
+from sqlobject.converters import registerConverter, sqlrepr, \
+ quote_str, unquote_str
from sqlobject.sqlbuilder import SQLExpression, SQLObjectField, \
Select, Insert, Update, Delete, Replace, \
- SQLTrueClauseClass, SQLConstant, SQLPrefix, SQLCall, SQLOp
-from sqlobject.converters import registerConverter
+ SQLTrueClauseClass, SQLConstant, SQLPrefix, SQLCall, SQLOp, \
+ _LikeQuoted
class TestClass:
@@ -41,23 +43,23 @@
assert sqlrepr('A String', 'firebird') == "'A String'"
def test_string_newline():
- assert sqlrepr('A String\nAnother', 'postgres') == "'A String\\nAnother'"
+ assert sqlrepr('A String\nAnother', 'postgres') == "E'A String\\nAnother'"
assert sqlrepr('A String\nAnother', 'sqlite') == "'A String\nAnother'"
def test_string_tab():
- assert sqlrepr('A String\tAnother', 'postgres') == "'A String\\tAnother'"
+ assert sqlrepr('A String\tAnother', 'postgres') == "E'A String\\tAnother'"
def test_string_r():
- assert sqlrepr('A String\rAnother', 'postgres') == "'A String\\rAnother'"
+ assert sqlrepr('A String\rAnother', 'postgres') == "E'A String\\rAnother'"
def test_string_b():
- assert sqlrepr('A String\bAnother', 'postgres') == "'A String\\bAnother'"
+ assert sqlrepr('A String\bAnother', 'postgres') == "E'A String\\bAnother'"
def test_string_000():
- assert sqlrepr('A String\000Another', 'postgres') == "'A String\\0Another'"
+ assert sqlrepr('A String\000Another', 'postgres') == "E'A String\\0Another'"
def test_string_():
- assert sqlrepr('A String\tAnother', 'postgres') == "'A String\\tAnother'"
+ assert sqlrepr('A String\tAnother', 'postgres') == "E'A String\\tAnother'"
assert sqlrepr('A String\'Another', 'firebird') == "'A String''Another'"
def test_simple_unicode():
@@ -200,3 +202,18 @@
def test_timedelta():
assert sqlrepr(timedelta(seconds=30*60)) == \
"INTERVAL '0 days 1800 seconds'"
+
+def test_quote_unquote_str():
+ assert quote_str('test%', 'postgres') == "'test%'"
+ assert quote_str('test%', 'sqlite') == "'test%'"
+ assert quote_str('test\%', 'postgres') == "E'test\\%'"
+ assert quote_str('test\\%', 'sqlite') == "'test\%'"
+ assert unquote_str("'test%'") == 'test%'
+ assert unquote_str("'test\\%'") == 'test\\%'
+ assert unquote_str("E'test\\%'") == 'test\\%'
+
+def test_like_quoted():
+ assert sqlrepr(_LikeQuoted('test'), 'postgres') == "'test'"
+ assert sqlrepr(_LikeQuoted('test'), 'sqlite') == "'test'"
+ assert sqlrepr(_LikeQuoted('test%'), 'postgres') == r"E'test\\%'"
+ assert sqlrepr(_LikeQuoted('test%'), 'sqlite') == r"'test\%'"
|