summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Kreen2008-03-11 16:17:07 +0000
committerMarko Kreen2008-03-11 16:17:07 +0000
commitf06eecab93dd29b8a4d1064732b39da4b16c2ce0 (patch)
tree14a0328403495fd8c66b88ab5bbd7522cec89125
parentd8119d54d3d79b5a01ece87e59e988099bd646d6 (diff)
unquote_ident() + unquote_literal()
-rw-r--r--python/modules/cquoting.c109
-rw-r--r--python/skytools/_pyquoting.py34
-rw-r--r--python/skytools/quoting.py9
3 files changed, 149 insertions, 3 deletions
diff --git a/python/modules/cquoting.c b/python/modules/cquoting.c
index ff06972b..a929233d 100644
--- a/python/modules/cquoting.c
+++ b/python/modules/cquoting.c
@@ -302,6 +302,114 @@ static PyObject *quote_bytea_raw(PyObject *self, PyObject *args)
return common_quote(args, quote_bytea_raw_body);
}
+/* SQL unquote */
+static const char doc_unquote_literal[] =
+"Unquote SQL quoted string.\n\n"
+"C implementation.";
+
+static PyObject *do_sql_ext(unsigned char *src, Py_ssize_t src_len)
+{
+ unsigned char *dst, *src_end = src + src_len;
+ struct Buf buf;
+
+ dst = buf_init(&buf, src_len);
+ if (!dst)
+ return NULL;
+
+ while (src < src_end) {
+ if (*src == '\'') {
+ src++;
+ if (src < src_end && *src == '\'') {
+ *dst++ = *src++;
+ continue;
+ }
+ goto failed;
+ }
+ if (*src != '\\') {
+ *dst++ = *src++;
+ continue;
+ }
+ if (++src >= src_end)
+ goto failed;
+ switch (*src) {
+ case 't': *dst++ = '\t'; src++; break;
+ case 'n': *dst++ = '\n'; src++; break;
+ case 'r': *dst++ = '\r'; src++; break;
+ case 'a': *dst++ = '\a'; src++; break;
+ case 'b': *dst++ = '\b'; src++; break;
+ default:
+ if (*src >= '0' && *src <= '7') {
+ unsigned char c = *src++ - '0';
+ if (src < src_end && *src >= '0' && *src <= '7') {
+ c = (c << 3) | ((*src++) - '0');
+ if (src < src_end && *src >= '0' && *src <= '7')
+ c = (c << 3) | ((*src++) - '0');
+ }
+ *dst++ = c;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ }
+ return buf_pystr(&buf, 0, dst);
+failed:
+ PyErr_Format(PyExc_ValueError, "Broken exteded SQL string");
+ return NULL;
+}
+
+static PyObject *do_sql_std(unsigned char *src, Py_ssize_t src_len)
+{
+ unsigned char *dst, *src_end = src + src_len;
+ struct Buf buf;
+
+ dst = buf_init(&buf, src_len);
+ if (!dst)
+ return NULL;
+
+ while (src < src_end) {
+ if (*src != '\'') {
+ *dst++ = *src++;
+ continue;
+ }
+ src++;
+ if (src >= src_end || *src != '\'')
+ goto failed;
+ *dst++ = *src++;
+ }
+ return buf_pystr(&buf, 0, dst);
+failed:
+ PyErr_Format(PyExc_ValueError, "Broken standard SQL string");
+ return NULL;
+}
+
+static PyObject *unquote_literal(PyObject *self, PyObject *args)
+{
+ unsigned char *src = NULL;
+ Py_ssize_t src_len = 0;
+ int stdstr = 0;
+ PyObject *value = NULL;
+ if (!PyArg_ParseTuple(args, "O|i", &value, &stdstr))
+ return NULL;
+ if (PyString_AsStringAndSize(value, (char **)&src, &src_len) < 0)
+ return NULL;
+ if (src_len == 4 && strcasecmp((char *)src, "null") == 0) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ if (src_len < 2 || src[src_len - 1] != '\'')
+ goto badstr;
+ if (src[0] == '\'') {
+ src++; src_len -= 2;
+ return stdstr ? do_sql_std(src, src_len) : do_sql_ext(src, src_len);
+ } else if (src_len > 2 && (src[0] | 0x20) == 'e' && src[1] == '\'') {
+ src += 2; src_len -= 3;
+ return do_sql_ext(src, src_len);
+ }
+badstr:
+ Py_INCREF(value);
+ return value;
+}
+
/* C unescape */
static const char doc_unescape[] =
"Unescape C-style escaped string.\n\n"
@@ -627,6 +735,7 @@ cquoting_methods[] = {
{ "unescape", unescape, METH_VARARGS, doc_unescape },
{ "db_urlencode", db_urlencode, METH_VARARGS, doc_db_urlencode },
{ "db_urldecode", db_urldecode, METH_VARARGS, doc_db_urldecode },
+ { "unquote_literal", unquote_literal, METH_VARARGS, doc_unquote_literal },
{ NULL }
};
diff --git a/python/skytools/_pyquoting.py b/python/skytools/_pyquoting.py
index 28a57577..2897c7f9 100644
--- a/python/skytools/_pyquoting.py
+++ b/python/skytools/_pyquoting.py
@@ -10,6 +10,7 @@ import urllib, re
__all__ = [
"quote_literal", "quote_copy", "quote_bytea_raw",
"db_urlencode", "db_urldecode", "unescape",
+ "unquote_literal",
]
#
@@ -135,7 +136,7 @@ _esc_map = {
'\\': '\\',
}
-def _sub_unescape(m):
+def _sub_unescape_c(m):
v = m.group(1)
if (len(v) == 1) and (v < '0' or v > '7'):
try:
@@ -149,5 +150,34 @@ def unescape(val):
"""Removes C-style escapes from string.
Python implementation.
"""
- return _esc_rc.sub(_sub_unescape, val)
+ return _esc_rc.sub(_sub_unescape_c, val)
+
+_esql_re = r"''|\\([0-7]{1,3}|.)"
+_esql_rc = re.compile(_esc_re)
+def _sub_unescape_sqlext(m):
+ if m.group() == "''":
+ return "'"
+ v = m.group(1)
+ if (len(v) == 1) and (v < '0' or v > '7'):
+ try:
+ return _esc_map[v]
+ except KeyError:
+ return v
+ return chr(int(v, 8))
+
+def unquote_literal(val, stdstr = False):
+ """Unquotes SQL string.
+ Ordinary '' quoted string can be both standard and extended quoted.
+ E'' quoting is always taken as extended quoted.
+ """
+ if val[0] == "'" and val[-1] == "'":
+ if stdstr:
+ return val[1:-1].replace("''", "'")
+ else:
+ return _esql_rc.sub(_sub_unescape_sqlext, val[1:-1])
+ elif len(val) > 2 and val[0] in ('E', 'e') and val[1] == "'" and val[-1] == "'":
+ return _esql_rc.sub(_sub_unescape_sqlext, val[2:-1])
+ elif val.tolower() == "null":
+ return None
+ return val
diff --git a/python/skytools/quoting.py b/python/skytools/quoting.py
index 19854a5f..10492b37 100644
--- a/python/skytools/quoting.py
+++ b/python/skytools/quoting.py
@@ -9,7 +9,8 @@ __all__ = [
"db_urlencode", "db_urldecode", "unescape",
"quote_bytea_literal", "quote_bytea_copy", "quote_statement",
- "quote_ident", "quote_fqident", "quote_json", "unescape_copy"
+ "quote_ident", "quote_fqident", "quote_json", "unescape_copy",
+ "unquote_ident", "unquote_literal",
]
try:
@@ -105,3 +106,9 @@ def unescape_copy(val):
return None
return unescape(val)
+def unquote_sql_ident(val):
+ """Unquotes possibly quoted SQL identifier."""
+ if val[0] == '"' and val[-1] == '"':
+ return val[1:-1].replace('""', '"')
+ return val
+