diff options
Diffstat (limited to 'lib/sqlalchemy/dialects/postgresql/hstore.py')
-rw-r--r-- | lib/sqlalchemy/dialects/postgresql/hstore.py | 129 |
1 files changed, 81 insertions, 48 deletions
diff --git a/lib/sqlalchemy/dialects/postgresql/hstore.py b/lib/sqlalchemy/dialects/postgresql/hstore.py index b6c9e7124..e4bac692a 100644 --- a/lib/sqlalchemy/dialects/postgresql/hstore.py +++ b/lib/sqlalchemy/dialects/postgresql/hstore.py @@ -14,38 +14,50 @@ from ...sql import functions as sqlfunc from ...sql import operators from ... import util -__all__ = ('HSTORE', 'hstore') +__all__ = ("HSTORE", "hstore") idx_precedence = operators._PRECEDENCE[operators.json_getitem_op] GETITEM = operators.custom_op( - "->", precedence=idx_precedence, natural_self_precedent=True, - eager_grouping=True + "->", + precedence=idx_precedence, + natural_self_precedent=True, + eager_grouping=True, ) HAS_KEY = operators.custom_op( - "?", precedence=idx_precedence, natural_self_precedent=True, - eager_grouping=True + "?", + precedence=idx_precedence, + natural_self_precedent=True, + eager_grouping=True, ) HAS_ALL = operators.custom_op( - "?&", precedence=idx_precedence, natural_self_precedent=True, - eager_grouping=True + "?&", + precedence=idx_precedence, + natural_self_precedent=True, + eager_grouping=True, ) HAS_ANY = operators.custom_op( - "?|", precedence=idx_precedence, natural_self_precedent=True, - eager_grouping=True + "?|", + precedence=idx_precedence, + natural_self_precedent=True, + eager_grouping=True, ) CONTAINS = operators.custom_op( - "@>", precedence=idx_precedence, natural_self_precedent=True, - eager_grouping=True + "@>", + precedence=idx_precedence, + natural_self_precedent=True, + eager_grouping=True, ) CONTAINED_BY = operators.custom_op( - "<@", precedence=idx_precedence, natural_self_precedent=True, - eager_grouping=True + "<@", + precedence=idx_precedence, + natural_self_precedent=True, + eager_grouping=True, ) @@ -122,7 +134,7 @@ class HSTORE(sqltypes.Indexable, sqltypes.Concatenable, sqltypes.TypeEngine): """ - __visit_name__ = 'HSTORE' + __visit_name__ = "HSTORE" hashable = False text_type = sqltypes.Text() @@ -139,7 +151,8 @@ class HSTORE(sqltypes.Indexable, sqltypes.Concatenable, sqltypes.TypeEngine): self.text_type = text_type class Comparator( - sqltypes.Indexable.Comparator, sqltypes.Concatenable.Comparator): + sqltypes.Indexable.Comparator, sqltypes.Concatenable.Comparator + ): """Define comparison operations for :class:`.HSTORE`.""" def has_key(self, other): @@ -169,7 +182,8 @@ class HSTORE(sqltypes.Indexable, sqltypes.Concatenable, sqltypes.TypeEngine): keys of the argument jsonb expression. """ return self.operate( - CONTAINED_BY, other, result_type=sqltypes.Boolean) + CONTAINED_BY, other, result_type=sqltypes.Boolean + ) def _setup_getitem(self, index): return GETITEM, index, self.type.text_type @@ -223,12 +237,15 @@ class HSTORE(sqltypes.Indexable, sqltypes.Concatenable, sqltypes.TypeEngine): return _serialize_hstore(value).encode(encoding) else: return value + else: + def process(value): if isinstance(value, dict): return _serialize_hstore(value) else: return value + return process def result_processor(self, dialect, coltype): @@ -240,16 +257,19 @@ class HSTORE(sqltypes.Indexable, sqltypes.Concatenable, sqltypes.TypeEngine): return _parse_hstore(value.decode(encoding)) else: return value + else: + def process(value): if value is not None: return _parse_hstore(value) else: return value + return process -ischema_names['hstore'] = HSTORE +ischema_names["hstore"] = HSTORE class hstore(sqlfunc.GenericFunction): @@ -279,43 +299,44 @@ class hstore(sqlfunc.GenericFunction): :class:`.HSTORE` - the PostgreSQL ``HSTORE`` datatype. """ + type = HSTORE - name = 'hstore' + name = "hstore" class _HStoreDefinedFunction(sqlfunc.GenericFunction): type = sqltypes.Boolean - name = 'defined' + name = "defined" class _HStoreDeleteFunction(sqlfunc.GenericFunction): type = HSTORE - name = 'delete' + name = "delete" class _HStoreSliceFunction(sqlfunc.GenericFunction): type = HSTORE - name = 'slice' + name = "slice" class _HStoreKeysFunction(sqlfunc.GenericFunction): type = ARRAY(sqltypes.Text) - name = 'akeys' + name = "akeys" class _HStoreValsFunction(sqlfunc.GenericFunction): type = ARRAY(sqltypes.Text) - name = 'avals' + name = "avals" class _HStoreArrayFunction(sqlfunc.GenericFunction): type = ARRAY(sqltypes.Text) - name = 'hstore_to_array' + name = "hstore_to_array" class _HStoreMatrixFunction(sqlfunc.GenericFunction): type = ARRAY(sqltypes.Text) - name = 'hstore_to_matrix' + name = "hstore_to_matrix" # @@ -326,7 +347,8 @@ class _HStoreMatrixFunction(sqlfunc.GenericFunction): # My best guess at the parsing rules of hstore literals, since no formal # grammar is given. This is mostly reverse engineered from PG's input parser # behavior. -HSTORE_PAIR_RE = re.compile(r""" +HSTORE_PAIR_RE = re.compile( + r""" ( "(?P<key> (\\ . | [^"])* )" # Quoted key ) @@ -335,11 +357,16 @@ HSTORE_PAIR_RE = re.compile(r""" (?P<value_null> NULL ) # NULL value | "(?P<value> (\\ . | [^"])* )" # Quoted value ) -""", re.VERBOSE) +""", + re.VERBOSE, +) -HSTORE_DELIMITER_RE = re.compile(r""" +HSTORE_DELIMITER_RE = re.compile( + r""" [ ]* , [ ]* -""", re.VERBOSE) +""", + re.VERBOSE, +) def _parse_error(hstore_str, pos): @@ -348,16 +375,19 @@ def _parse_error(hstore_str, pos): ctx = 20 hslen = len(hstore_str) - parsed_tail = hstore_str[max(pos - ctx - 1, 0):min(pos, hslen)] - residual = hstore_str[min(pos, hslen):min(pos + ctx + 1, hslen)] + parsed_tail = hstore_str[max(pos - ctx - 1, 0) : min(pos, hslen)] + residual = hstore_str[min(pos, hslen) : min(pos + ctx + 1, hslen)] if len(parsed_tail) > ctx: - parsed_tail = '[...]' + parsed_tail[1:] + parsed_tail = "[...]" + parsed_tail[1:] if len(residual) > ctx: - residual = residual[:-1] + '[...]' + residual = residual[:-1] + "[...]" return "After %r, could not parse residual at position %d: %r" % ( - parsed_tail, pos, residual) + parsed_tail, + pos, + residual, + ) def _parse_hstore(hstore_str): @@ -377,13 +407,15 @@ def _parse_hstore(hstore_str): pair_match = HSTORE_PAIR_RE.match(hstore_str) while pair_match is not None: - key = pair_match.group('key').replace(r'\"', '"').replace( - "\\\\", "\\") - if pair_match.group('value_null'): + key = pair_match.group("key").replace(r"\"", '"').replace("\\\\", "\\") + if pair_match.group("value_null"): value = None else: - value = pair_match.group('value').replace( - r'\"', '"').replace("\\\\", "\\") + value = ( + pair_match.group("value") + .replace(r"\"", '"') + .replace("\\\\", "\\") + ) result[key] = value pos += pair_match.end() @@ -405,16 +437,17 @@ def _serialize_hstore(val): both be strings (except None for values). """ + def esc(s, position): - if position == 'value' and s is None: - return 'NULL' + if position == "value" and s is None: + return "NULL" elif isinstance(s, util.string_types): - return '"%s"' % s.replace("\\", "\\\\").replace('"', r'\"') + return '"%s"' % s.replace("\\", "\\\\").replace('"', r"\"") else: - raise ValueError("%r in %s position is not a string." % - (s, position)) - - return ', '.join('%s=>%s' % (esc(k, 'key'), esc(v, 'value')) - for k, v in val.items()) - + raise ValueError( + "%r in %s position is not a string." % (s, position) + ) + return ", ".join( + "%s=>%s" % (esc(k, "key"), esc(v, "value")) for k, v in val.items() + ) |