You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

774 lines
24KB

  1. # mysql/types.py
  2. # Copyright (C) 2005-2021 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
  7. import datetime
  8. from ... import exc
  9. from ... import types as sqltypes
  10. from ... import util
  11. class _NumericType(object):
  12. """Base for MySQL numeric types.
  13. This is the base both for NUMERIC as well as INTEGER, hence
  14. it's a mixin.
  15. """
  16. def __init__(self, unsigned=False, zerofill=False, **kw):
  17. self.unsigned = unsigned
  18. self.zerofill = zerofill
  19. super(_NumericType, self).__init__(**kw)
  20. def __repr__(self):
  21. return util.generic_repr(
  22. self, to_inspect=[_NumericType, sqltypes.Numeric]
  23. )
  24. class _FloatType(_NumericType, sqltypes.Float):
  25. def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
  26. if isinstance(self, (REAL, DOUBLE)) and (
  27. (precision is None and scale is not None)
  28. or (precision is not None and scale is None)
  29. ):
  30. raise exc.ArgumentError(
  31. "You must specify both precision and scale or omit "
  32. "both altogether."
  33. )
  34. super(_FloatType, self).__init__(
  35. precision=precision, asdecimal=asdecimal, **kw
  36. )
  37. self.scale = scale
  38. def __repr__(self):
  39. return util.generic_repr(
  40. self, to_inspect=[_FloatType, _NumericType, sqltypes.Float]
  41. )
  42. class _IntegerType(_NumericType, sqltypes.Integer):
  43. def __init__(self, display_width=None, **kw):
  44. self.display_width = display_width
  45. super(_IntegerType, self).__init__(**kw)
  46. def __repr__(self):
  47. return util.generic_repr(
  48. self, to_inspect=[_IntegerType, _NumericType, sqltypes.Integer]
  49. )
  50. class _StringType(sqltypes.String):
  51. """Base for MySQL string types."""
  52. def __init__(
  53. self,
  54. charset=None,
  55. collation=None,
  56. ascii=False, # noqa
  57. binary=False,
  58. unicode=False,
  59. national=False,
  60. **kw
  61. ):
  62. self.charset = charset
  63. # allow collate= or collation=
  64. kw.setdefault("collation", kw.pop("collate", collation))
  65. self.ascii = ascii
  66. self.unicode = unicode
  67. self.binary = binary
  68. self.national = national
  69. super(_StringType, self).__init__(**kw)
  70. def __repr__(self):
  71. return util.generic_repr(
  72. self, to_inspect=[_StringType, sqltypes.String]
  73. )
  74. class _MatchType(sqltypes.Float, sqltypes.MatchType):
  75. def __init__(self, **kw):
  76. # TODO: float arguments?
  77. sqltypes.Float.__init__(self)
  78. sqltypes.MatchType.__init__(self)
  79. class NUMERIC(_NumericType, sqltypes.NUMERIC):
  80. """MySQL NUMERIC type."""
  81. __visit_name__ = "NUMERIC"
  82. def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
  83. """Construct a NUMERIC.
  84. :param precision: Total digits in this number. If scale and precision
  85. are both None, values are stored to limits allowed by the server.
  86. :param scale: The number of digits after the decimal point.
  87. :param unsigned: a boolean, optional.
  88. :param zerofill: Optional. If true, values will be stored as strings
  89. left-padded with zeros. Note that this does not effect the values
  90. returned by the underlying database API, which continue to be
  91. numeric.
  92. """
  93. super(NUMERIC, self).__init__(
  94. precision=precision, scale=scale, asdecimal=asdecimal, **kw
  95. )
  96. class DECIMAL(_NumericType, sqltypes.DECIMAL):
  97. """MySQL DECIMAL type."""
  98. __visit_name__ = "DECIMAL"
  99. def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
  100. """Construct a DECIMAL.
  101. :param precision: Total digits in this number. If scale and precision
  102. are both None, values are stored to limits allowed by the server.
  103. :param scale: The number of digits after the decimal point.
  104. :param unsigned: a boolean, optional.
  105. :param zerofill: Optional. If true, values will be stored as strings
  106. left-padded with zeros. Note that this does not effect the values
  107. returned by the underlying database API, which continue to be
  108. numeric.
  109. """
  110. super(DECIMAL, self).__init__(
  111. precision=precision, scale=scale, asdecimal=asdecimal, **kw
  112. )
  113. class DOUBLE(_FloatType):
  114. """MySQL DOUBLE type."""
  115. __visit_name__ = "DOUBLE"
  116. def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
  117. """Construct a DOUBLE.
  118. .. note::
  119. The :class:`.DOUBLE` type by default converts from float
  120. to Decimal, using a truncation that defaults to 10 digits.
  121. Specify either ``scale=n`` or ``decimal_return_scale=n`` in order
  122. to change this scale, or ``asdecimal=False`` to return values
  123. directly as Python floating points.
  124. :param precision: Total digits in this number. If scale and precision
  125. are both None, values are stored to limits allowed by the server.
  126. :param scale: The number of digits after the decimal point.
  127. :param unsigned: a boolean, optional.
  128. :param zerofill: Optional. If true, values will be stored as strings
  129. left-padded with zeros. Note that this does not effect the values
  130. returned by the underlying database API, which continue to be
  131. numeric.
  132. """
  133. super(DOUBLE, self).__init__(
  134. precision=precision, scale=scale, asdecimal=asdecimal, **kw
  135. )
  136. class REAL(_FloatType, sqltypes.REAL):
  137. """MySQL REAL type."""
  138. __visit_name__ = "REAL"
  139. def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
  140. """Construct a REAL.
  141. .. note::
  142. The :class:`.REAL` type by default converts from float
  143. to Decimal, using a truncation that defaults to 10 digits.
  144. Specify either ``scale=n`` or ``decimal_return_scale=n`` in order
  145. to change this scale, or ``asdecimal=False`` to return values
  146. directly as Python floating points.
  147. :param precision: Total digits in this number. If scale and precision
  148. are both None, values are stored to limits allowed by the server.
  149. :param scale: The number of digits after the decimal point.
  150. :param unsigned: a boolean, optional.
  151. :param zerofill: Optional. If true, values will be stored as strings
  152. left-padded with zeros. Note that this does not effect the values
  153. returned by the underlying database API, which continue to be
  154. numeric.
  155. """
  156. super(REAL, self).__init__(
  157. precision=precision, scale=scale, asdecimal=asdecimal, **kw
  158. )
  159. class FLOAT(_FloatType, sqltypes.FLOAT):
  160. """MySQL FLOAT type."""
  161. __visit_name__ = "FLOAT"
  162. def __init__(self, precision=None, scale=None, asdecimal=False, **kw):
  163. """Construct a FLOAT.
  164. :param precision: Total digits in this number. If scale and precision
  165. are both None, values are stored to limits allowed by the server.
  166. :param scale: The number of digits after the decimal point.
  167. :param unsigned: a boolean, optional.
  168. :param zerofill: Optional. If true, values will be stored as strings
  169. left-padded with zeros. Note that this does not effect the values
  170. returned by the underlying database API, which continue to be
  171. numeric.
  172. """
  173. super(FLOAT, self).__init__(
  174. precision=precision, scale=scale, asdecimal=asdecimal, **kw
  175. )
  176. def bind_processor(self, dialect):
  177. return None
  178. class INTEGER(_IntegerType, sqltypes.INTEGER):
  179. """MySQL INTEGER type."""
  180. __visit_name__ = "INTEGER"
  181. def __init__(self, display_width=None, **kw):
  182. """Construct an INTEGER.
  183. :param display_width: Optional, maximum display width for this number.
  184. :param unsigned: a boolean, optional.
  185. :param zerofill: Optional. If true, values will be stored as strings
  186. left-padded with zeros. Note that this does not effect the values
  187. returned by the underlying database API, which continue to be
  188. numeric.
  189. """
  190. super(INTEGER, self).__init__(display_width=display_width, **kw)
  191. class BIGINT(_IntegerType, sqltypes.BIGINT):
  192. """MySQL BIGINTEGER type."""
  193. __visit_name__ = "BIGINT"
  194. def __init__(self, display_width=None, **kw):
  195. """Construct a BIGINTEGER.
  196. :param display_width: Optional, maximum display width for this number.
  197. :param unsigned: a boolean, optional.
  198. :param zerofill: Optional. If true, values will be stored as strings
  199. left-padded with zeros. Note that this does not effect the values
  200. returned by the underlying database API, which continue to be
  201. numeric.
  202. """
  203. super(BIGINT, self).__init__(display_width=display_width, **kw)
  204. class MEDIUMINT(_IntegerType):
  205. """MySQL MEDIUMINTEGER type."""
  206. __visit_name__ = "MEDIUMINT"
  207. def __init__(self, display_width=None, **kw):
  208. """Construct a MEDIUMINTEGER
  209. :param display_width: Optional, maximum display width for this number.
  210. :param unsigned: a boolean, optional.
  211. :param zerofill: Optional. If true, values will be stored as strings
  212. left-padded with zeros. Note that this does not effect the values
  213. returned by the underlying database API, which continue to be
  214. numeric.
  215. """
  216. super(MEDIUMINT, self).__init__(display_width=display_width, **kw)
  217. class TINYINT(_IntegerType):
  218. """MySQL TINYINT type."""
  219. __visit_name__ = "TINYINT"
  220. def __init__(self, display_width=None, **kw):
  221. """Construct a TINYINT.
  222. :param display_width: Optional, maximum display width for this number.
  223. :param unsigned: a boolean, optional.
  224. :param zerofill: Optional. If true, values will be stored as strings
  225. left-padded with zeros. Note that this does not effect the values
  226. returned by the underlying database API, which continue to be
  227. numeric.
  228. """
  229. super(TINYINT, self).__init__(display_width=display_width, **kw)
  230. class SMALLINT(_IntegerType, sqltypes.SMALLINT):
  231. """MySQL SMALLINTEGER type."""
  232. __visit_name__ = "SMALLINT"
  233. def __init__(self, display_width=None, **kw):
  234. """Construct a SMALLINTEGER.
  235. :param display_width: Optional, maximum display width for this number.
  236. :param unsigned: a boolean, optional.
  237. :param zerofill: Optional. If true, values will be stored as strings
  238. left-padded with zeros. Note that this does not effect the values
  239. returned by the underlying database API, which continue to be
  240. numeric.
  241. """
  242. super(SMALLINT, self).__init__(display_width=display_width, **kw)
  243. class BIT(sqltypes.TypeEngine):
  244. """MySQL BIT type.
  245. This type is for MySQL 5.0.3 or greater for MyISAM, and 5.0.5 or greater
  246. for MyISAM, MEMORY, InnoDB and BDB. For older versions, use a
  247. MSTinyInteger() type.
  248. """
  249. __visit_name__ = "BIT"
  250. def __init__(self, length=None):
  251. """Construct a BIT.
  252. :param length: Optional, number of bits.
  253. """
  254. self.length = length
  255. def result_processor(self, dialect, coltype):
  256. """Convert a MySQL's 64 bit, variable length binary string to a long.
  257. TODO: this is MySQL-db, pyodbc specific. OurSQL and mysqlconnector
  258. already do this, so this logic should be moved to those dialects.
  259. """
  260. def process(value):
  261. if value is not None:
  262. v = 0
  263. for i in value:
  264. if not isinstance(i, int):
  265. i = ord(i) # convert byte to int on Python 2
  266. v = v << 8 | i
  267. return v
  268. return value
  269. return process
  270. class TIME(sqltypes.TIME):
  271. """MySQL TIME type."""
  272. __visit_name__ = "TIME"
  273. def __init__(self, timezone=False, fsp=None):
  274. """Construct a MySQL TIME type.
  275. :param timezone: not used by the MySQL dialect.
  276. :param fsp: fractional seconds precision value.
  277. MySQL 5.6 supports storage of fractional seconds;
  278. this parameter will be used when emitting DDL
  279. for the TIME type.
  280. .. note::
  281. DBAPI driver support for fractional seconds may
  282. be limited; current support includes
  283. MySQL Connector/Python.
  284. """
  285. super(TIME, self).__init__(timezone=timezone)
  286. self.fsp = fsp
  287. def result_processor(self, dialect, coltype):
  288. time = datetime.time
  289. def process(value):
  290. # convert from a timedelta value
  291. if value is not None:
  292. microseconds = value.microseconds
  293. seconds = value.seconds
  294. minutes = seconds // 60
  295. return time(
  296. minutes // 60,
  297. minutes % 60,
  298. seconds - minutes * 60,
  299. microsecond=microseconds,
  300. )
  301. else:
  302. return None
  303. return process
  304. class TIMESTAMP(sqltypes.TIMESTAMP):
  305. """MySQL TIMESTAMP type."""
  306. __visit_name__ = "TIMESTAMP"
  307. def __init__(self, timezone=False, fsp=None):
  308. """Construct a MySQL TIMESTAMP type.
  309. :param timezone: not used by the MySQL dialect.
  310. :param fsp: fractional seconds precision value.
  311. MySQL 5.6.4 supports storage of fractional seconds;
  312. this parameter will be used when emitting DDL
  313. for the TIMESTAMP type.
  314. .. note::
  315. DBAPI driver support for fractional seconds may
  316. be limited; current support includes
  317. MySQL Connector/Python.
  318. """
  319. super(TIMESTAMP, self).__init__(timezone=timezone)
  320. self.fsp = fsp
  321. class DATETIME(sqltypes.DATETIME):
  322. """MySQL DATETIME type."""
  323. __visit_name__ = "DATETIME"
  324. def __init__(self, timezone=False, fsp=None):
  325. """Construct a MySQL DATETIME type.
  326. :param timezone: not used by the MySQL dialect.
  327. :param fsp: fractional seconds precision value.
  328. MySQL 5.6.4 supports storage of fractional seconds;
  329. this parameter will be used when emitting DDL
  330. for the DATETIME type.
  331. .. note::
  332. DBAPI driver support for fractional seconds may
  333. be limited; current support includes
  334. MySQL Connector/Python.
  335. """
  336. super(DATETIME, self).__init__(timezone=timezone)
  337. self.fsp = fsp
  338. class YEAR(sqltypes.TypeEngine):
  339. """MySQL YEAR type, for single byte storage of years 1901-2155."""
  340. __visit_name__ = "YEAR"
  341. def __init__(self, display_width=None):
  342. self.display_width = display_width
  343. class TEXT(_StringType, sqltypes.TEXT):
  344. """MySQL TEXT type, for text up to 2^16 characters."""
  345. __visit_name__ = "TEXT"
  346. def __init__(self, length=None, **kw):
  347. """Construct a TEXT.
  348. :param length: Optional, if provided the server may optimize storage
  349. by substituting the smallest TEXT type sufficient to store
  350. ``length`` characters.
  351. :param charset: Optional, a column-level character set for this string
  352. value. Takes precedence to 'ascii' or 'unicode' short-hand.
  353. :param collation: Optional, a column-level collation for this string
  354. value. Takes precedence to 'binary' short-hand.
  355. :param ascii: Defaults to False: short-hand for the ``latin1``
  356. character set, generates ASCII in schema.
  357. :param unicode: Defaults to False: short-hand for the ``ucs2``
  358. character set, generates UNICODE in schema.
  359. :param national: Optional. If true, use the server's configured
  360. national character set.
  361. :param binary: Defaults to False: short-hand, pick the binary
  362. collation type that matches the column's character set. Generates
  363. BINARY in schema. This does not affect the type of data stored,
  364. only the collation of character data.
  365. """
  366. super(TEXT, self).__init__(length=length, **kw)
  367. class TINYTEXT(_StringType):
  368. """MySQL TINYTEXT type, for text up to 2^8 characters."""
  369. __visit_name__ = "TINYTEXT"
  370. def __init__(self, **kwargs):
  371. """Construct a TINYTEXT.
  372. :param charset: Optional, a column-level character set for this string
  373. value. Takes precedence to 'ascii' or 'unicode' short-hand.
  374. :param collation: Optional, a column-level collation for this string
  375. value. Takes precedence to 'binary' short-hand.
  376. :param ascii: Defaults to False: short-hand for the ``latin1``
  377. character set, generates ASCII in schema.
  378. :param unicode: Defaults to False: short-hand for the ``ucs2``
  379. character set, generates UNICODE in schema.
  380. :param national: Optional. If true, use the server's configured
  381. national character set.
  382. :param binary: Defaults to False: short-hand, pick the binary
  383. collation type that matches the column's character set. Generates
  384. BINARY in schema. This does not affect the type of data stored,
  385. only the collation of character data.
  386. """
  387. super(TINYTEXT, self).__init__(**kwargs)
  388. class MEDIUMTEXT(_StringType):
  389. """MySQL MEDIUMTEXT type, for text up to 2^24 characters."""
  390. __visit_name__ = "MEDIUMTEXT"
  391. def __init__(self, **kwargs):
  392. """Construct a MEDIUMTEXT.
  393. :param charset: Optional, a column-level character set for this string
  394. value. Takes precedence to 'ascii' or 'unicode' short-hand.
  395. :param collation: Optional, a column-level collation for this string
  396. value. Takes precedence to 'binary' short-hand.
  397. :param ascii: Defaults to False: short-hand for the ``latin1``
  398. character set, generates ASCII in schema.
  399. :param unicode: Defaults to False: short-hand for the ``ucs2``
  400. character set, generates UNICODE in schema.
  401. :param national: Optional. If true, use the server's configured
  402. national character set.
  403. :param binary: Defaults to False: short-hand, pick the binary
  404. collation type that matches the column's character set. Generates
  405. BINARY in schema. This does not affect the type of data stored,
  406. only the collation of character data.
  407. """
  408. super(MEDIUMTEXT, self).__init__(**kwargs)
  409. class LONGTEXT(_StringType):
  410. """MySQL LONGTEXT type, for text up to 2^32 characters."""
  411. __visit_name__ = "LONGTEXT"
  412. def __init__(self, **kwargs):
  413. """Construct a LONGTEXT.
  414. :param charset: Optional, a column-level character set for this string
  415. value. Takes precedence to 'ascii' or 'unicode' short-hand.
  416. :param collation: Optional, a column-level collation for this string
  417. value. Takes precedence to 'binary' short-hand.
  418. :param ascii: Defaults to False: short-hand for the ``latin1``
  419. character set, generates ASCII in schema.
  420. :param unicode: Defaults to False: short-hand for the ``ucs2``
  421. character set, generates UNICODE in schema.
  422. :param national: Optional. If true, use the server's configured
  423. national character set.
  424. :param binary: Defaults to False: short-hand, pick the binary
  425. collation type that matches the column's character set. Generates
  426. BINARY in schema. This does not affect the type of data stored,
  427. only the collation of character data.
  428. """
  429. super(LONGTEXT, self).__init__(**kwargs)
  430. class VARCHAR(_StringType, sqltypes.VARCHAR):
  431. """MySQL VARCHAR type, for variable-length character data."""
  432. __visit_name__ = "VARCHAR"
  433. def __init__(self, length=None, **kwargs):
  434. """Construct a VARCHAR.
  435. :param charset: Optional, a column-level character set for this string
  436. value. Takes precedence to 'ascii' or 'unicode' short-hand.
  437. :param collation: Optional, a column-level collation for this string
  438. value. Takes precedence to 'binary' short-hand.
  439. :param ascii: Defaults to False: short-hand for the ``latin1``
  440. character set, generates ASCII in schema.
  441. :param unicode: Defaults to False: short-hand for the ``ucs2``
  442. character set, generates UNICODE in schema.
  443. :param national: Optional. If true, use the server's configured
  444. national character set.
  445. :param binary: Defaults to False: short-hand, pick the binary
  446. collation type that matches the column's character set. Generates
  447. BINARY in schema. This does not affect the type of data stored,
  448. only the collation of character data.
  449. """
  450. super(VARCHAR, self).__init__(length=length, **kwargs)
  451. class CHAR(_StringType, sqltypes.CHAR):
  452. """MySQL CHAR type, for fixed-length character data."""
  453. __visit_name__ = "CHAR"
  454. def __init__(self, length=None, **kwargs):
  455. """Construct a CHAR.
  456. :param length: Maximum data length, in characters.
  457. :param binary: Optional, use the default binary collation for the
  458. national character set. This does not affect the type of data
  459. stored, use a BINARY type for binary data.
  460. :param collation: Optional, request a particular collation. Must be
  461. compatible with the national character set.
  462. """
  463. super(CHAR, self).__init__(length=length, **kwargs)
  464. @classmethod
  465. def _adapt_string_for_cast(self, type_):
  466. # copy the given string type into a CHAR
  467. # for the purposes of rendering a CAST expression
  468. type_ = sqltypes.to_instance(type_)
  469. if isinstance(type_, sqltypes.CHAR):
  470. return type_
  471. elif isinstance(type_, _StringType):
  472. return CHAR(
  473. length=type_.length,
  474. charset=type_.charset,
  475. collation=type_.collation,
  476. ascii=type_.ascii,
  477. binary=type_.binary,
  478. unicode=type_.unicode,
  479. national=False, # not supported in CAST
  480. )
  481. else:
  482. return CHAR(length=type_.length)
  483. class NVARCHAR(_StringType, sqltypes.NVARCHAR):
  484. """MySQL NVARCHAR type.
  485. For variable-length character data in the server's configured national
  486. character set.
  487. """
  488. __visit_name__ = "NVARCHAR"
  489. def __init__(self, length=None, **kwargs):
  490. """Construct an NVARCHAR.
  491. :param length: Maximum data length, in characters.
  492. :param binary: Optional, use the default binary collation for the
  493. national character set. This does not affect the type of data
  494. stored, use a BINARY type for binary data.
  495. :param collation: Optional, request a particular collation. Must be
  496. compatible with the national character set.
  497. """
  498. kwargs["national"] = True
  499. super(NVARCHAR, self).__init__(length=length, **kwargs)
  500. class NCHAR(_StringType, sqltypes.NCHAR):
  501. """MySQL NCHAR type.
  502. For fixed-length character data in the server's configured national
  503. character set.
  504. """
  505. __visit_name__ = "NCHAR"
  506. def __init__(self, length=None, **kwargs):
  507. """Construct an NCHAR.
  508. :param length: Maximum data length, in characters.
  509. :param binary: Optional, use the default binary collation for the
  510. national character set. This does not affect the type of data
  511. stored, use a BINARY type for binary data.
  512. :param collation: Optional, request a particular collation. Must be
  513. compatible with the national character set.
  514. """
  515. kwargs["national"] = True
  516. super(NCHAR, self).__init__(length=length, **kwargs)
  517. class TINYBLOB(sqltypes._Binary):
  518. """MySQL TINYBLOB type, for binary data up to 2^8 bytes."""
  519. __visit_name__ = "TINYBLOB"
  520. class MEDIUMBLOB(sqltypes._Binary):
  521. """MySQL MEDIUMBLOB type, for binary data up to 2^24 bytes."""
  522. __visit_name__ = "MEDIUMBLOB"
  523. class LONGBLOB(sqltypes._Binary):
  524. """MySQL LONGBLOB type, for binary data up to 2^32 bytes."""
  525. __visit_name__ = "LONGBLOB"