Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/sqlalchemy/dialects/mysql/enumerated.py : 21%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# mysql/enumerated.py
2# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: http://www.opensource.org/licenses/mit-license.php
8import re
10from .types import _StringType
11from ... import exc
12from ... import sql
13from ... import util
14from ...sql import sqltypes
17class _EnumeratedValues(_StringType):
18 def _init_values(self, values, kw):
19 self.quoting = kw.pop("quoting", "auto")
21 if self.quoting == "auto" and len(values):
22 # What quoting character are we using?
23 q = None
24 for e in values:
25 if len(e) == 0:
26 self.quoting = "unquoted"
27 break
28 elif q is None:
29 q = e[0]
31 if len(e) == 1 or e[0] != q or e[-1] != q:
32 self.quoting = "unquoted"
33 break
34 else:
35 self.quoting = "quoted"
37 if self.quoting == "quoted":
38 util.warn_deprecated(
39 "Manually quoting %s value literals is deprecated. Supply "
40 "unquoted values and use the quoting= option in cases of "
41 "ambiguity." % self.__class__.__name__
42 )
44 values = self._strip_values(values)
46 self._enumerated_values = values
47 length = max([len(v) for v in values] + [0])
48 return values, length
50 @classmethod
51 def _strip_values(cls, values):
52 strip_values = []
53 for a in values:
54 if a[0:1] == '"' or a[0:1] == "'":
55 # strip enclosing quotes and unquote interior
56 a = a[1:-1].replace(a[0] * 2, a[0])
57 strip_values.append(a)
58 return strip_values
61class ENUM(sqltypes.NativeForEmulated, sqltypes.Enum, _EnumeratedValues):
62 """MySQL ENUM type."""
64 __visit_name__ = "ENUM"
66 native_enum = True
68 def __init__(self, *enums, **kw):
69 """Construct an ENUM.
71 E.g.::
73 Column('myenum', ENUM("foo", "bar", "baz"))
75 :param enums: The range of valid values for this ENUM. Values will be
76 quoted when generating the schema according to the quoting flag (see
77 below). This object may also be a PEP-435-compliant enumerated
78 type.
80 .. versionadded: 1.1 added support for PEP-435-compliant enumerated
81 types.
83 :param strict: This flag has no effect.
85 .. versionchanged:: The MySQL ENUM type as well as the base Enum
86 type now validates all Python data values.
88 :param charset: Optional, a column-level character set for this string
89 value. Takes precedence to 'ascii' or 'unicode' short-hand.
91 :param collation: Optional, a column-level collation for this string
92 value. Takes precedence to 'binary' short-hand.
94 :param ascii: Defaults to False: short-hand for the ``latin1``
95 character set, generates ASCII in schema.
97 :param unicode: Defaults to False: short-hand for the ``ucs2``
98 character set, generates UNICODE in schema.
100 :param binary: Defaults to False: short-hand, pick the binary
101 collation type that matches the column's character set. Generates
102 BINARY in schema. This does not affect the type of data stored,
103 only the collation of character data.
105 :param quoting: Defaults to 'auto': automatically determine enum value
106 quoting. If all enum values are surrounded by the same quoting
107 character, then use 'quoted' mode. Otherwise, use 'unquoted' mode.
109 'quoted': values in enums are already quoted, they will be used
110 directly when generating the schema - this usage is deprecated.
112 'unquoted': values in enums are not quoted, they will be escaped and
113 surrounded by single quotes when generating the schema.
115 Previous versions of this type always required manually quoted
116 values to be supplied; future versions will always quote the string
117 literals for you. This is a transitional option.
119 """
121 kw.pop("strict", None)
122 self._enum_init(enums, kw)
123 _StringType.__init__(self, length=self.length, **kw)
125 @classmethod
126 def adapt_emulated_to_native(cls, impl, **kw):
127 """Produce a MySQL native :class:`.mysql.ENUM` from plain
128 :class:`.Enum`.
130 """
131 kw.setdefault("validate_strings", impl.validate_strings)
132 kw.setdefault("values_callable", impl.values_callable)
133 return cls(**kw)
135 def _setup_for_values(self, values, objects, kw):
136 values, length = self._init_values(values, kw)
137 return super(ENUM, self)._setup_for_values(values, objects, kw)
139 def _object_value_for_elem(self, elem):
140 # mysql sends back a blank string for any value that
141 # was persisted that was not in the enums; that is, it does no
142 # validation on the incoming data, it "truncates" it to be
143 # the blank string. Return it straight.
144 if elem == "":
145 return elem
146 else:
147 return super(ENUM, self)._object_value_for_elem(elem)
149 def __repr__(self):
150 return util.generic_repr(
151 self, to_inspect=[ENUM, _StringType, sqltypes.Enum]
152 )
155class SET(_EnumeratedValues):
156 """MySQL SET type."""
158 __visit_name__ = "SET"
160 def __init__(self, *values, **kw):
161 """Construct a SET.
163 E.g.::
165 Column('myset', SET("foo", "bar", "baz"))
168 The list of potential values is required in the case that this
169 set will be used to generate DDL for a table, or if the
170 :paramref:`.SET.retrieve_as_bitwise` flag is set to True.
172 :param values: The range of valid values for this SET.
174 :param convert_unicode: Same flag as that of
175 :paramref:`.String.convert_unicode`.
177 :param collation: same as that of :paramref:`.String.collation`
179 :param charset: same as that of :paramref:`.VARCHAR.charset`.
181 :param ascii: same as that of :paramref:`.VARCHAR.ascii`.
183 :param unicode: same as that of :paramref:`.VARCHAR.unicode`.
185 :param binary: same as that of :paramref:`.VARCHAR.binary`.
187 :param quoting: Defaults to 'auto': automatically determine set value
188 quoting. If all values are surrounded by the same quoting
189 character, then use 'quoted' mode. Otherwise, use 'unquoted' mode.
191 'quoted': values in enums are already quoted, they will be used
192 directly when generating the schema - this usage is deprecated.
194 'unquoted': values in enums are not quoted, they will be escaped and
195 surrounded by single quotes when generating the schema.
197 Previous versions of this type always required manually quoted
198 values to be supplied; future versions will always quote the string
199 literals for you. This is a transitional option.
201 .. versionadded:: 0.9.0
203 :param retrieve_as_bitwise: if True, the data for the set type will be
204 persisted and selected using an integer value, where a set is coerced
205 into a bitwise mask for persistence. MySQL allows this mode which
206 has the advantage of being able to store values unambiguously,
207 such as the blank string ``''``. The datatype will appear
208 as the expression ``col + 0`` in a SELECT statement, so that the
209 value is coerced into an integer value in result sets.
210 This flag is required if one wishes
211 to persist a set that can store the blank string ``''`` as a value.
213 .. warning::
215 When using :paramref:`.mysql.SET.retrieve_as_bitwise`, it is
216 essential that the list of set values is expressed in the
217 **exact same order** as exists on the MySQL database.
219 .. versionadded:: 1.0.0
222 """
223 self.retrieve_as_bitwise = kw.pop("retrieve_as_bitwise", False)
224 values, length = self._init_values(values, kw)
225 self.values = tuple(values)
226 if not self.retrieve_as_bitwise and "" in values:
227 raise exc.ArgumentError(
228 "Can't use the blank value '' in a SET without "
229 "setting retrieve_as_bitwise=True"
230 )
231 if self.retrieve_as_bitwise:
232 self._bitmap = dict(
233 (value, 2 ** idx) for idx, value in enumerate(self.values)
234 )
235 self._bitmap.update(
236 (2 ** idx, value) for idx, value in enumerate(self.values)
237 )
238 kw.setdefault("length", length)
239 super(SET, self).__init__(**kw)
241 def column_expression(self, colexpr):
242 if self.retrieve_as_bitwise:
243 return sql.type_coerce(
244 sql.type_coerce(colexpr, sqltypes.Integer) + 0, self
245 )
246 else:
247 return colexpr
249 def result_processor(self, dialect, coltype):
250 if self.retrieve_as_bitwise:
252 def process(value):
253 if value is not None:
254 value = int(value)
256 return set(util.map_bits(self._bitmap.__getitem__, value))
257 else:
258 return None
260 else:
261 super_convert = super(SET, self).result_processor(dialect, coltype)
263 def process(value):
264 if isinstance(value, util.string_types):
265 # MySQLdb returns a string, let's parse
266 if super_convert:
267 value = super_convert(value)
268 return set(re.findall(r"[^,]+", value))
269 else:
270 # mysql-connector-python does a naive
271 # split(",") which throws in an empty string
272 if value is not None:
273 value.discard("")
274 return value
276 return process
278 def bind_processor(self, dialect):
279 super_convert = super(SET, self).bind_processor(dialect)
280 if self.retrieve_as_bitwise:
282 def process(value):
283 if value is None:
284 return None
285 elif isinstance(value, util.int_types + util.string_types):
286 if super_convert:
287 return super_convert(value)
288 else:
289 return value
290 else:
291 int_value = 0
292 for v in value:
293 int_value |= self._bitmap[v]
294 return int_value
296 else:
298 def process(value):
299 # accept strings and int (actually bitflag) values directly
300 if value is not None and not isinstance(
301 value, util.int_types + util.string_types
302 ):
303 value = ",".join(value)
305 if super_convert:
306 return super_convert(value)
307 else:
308 return value
310 return process
312 def adapt(self, impltype, **kw):
313 kw["retrieve_as_bitwise"] = self.retrieve_as_bitwise
314 return util.constructor_copy(self, impltype, *self.values, **kw)