diff options
author | Marko Kreen | 2008-03-11 16:17:07 +0000 |
---|---|---|
committer | Marko Kreen | 2008-03-11 16:17:07 +0000 |
commit | f06eecab93dd29b8a4d1064732b39da4b16c2ce0 (patch) | |
tree | 14a0328403495fd8c66b88ab5bbd7522cec89125 | |
parent | d8119d54d3d79b5a01ece87e59e988099bd646d6 (diff) |
unquote_ident() + unquote_literal()
-rw-r--r-- | python/modules/cquoting.c | 109 | ||||
-rw-r--r-- | python/skytools/_pyquoting.py | 34 | ||||
-rw-r--r-- | python/skytools/quoting.py | 9 |
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 + |