sql.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. """SQL composition utility module
  2. """
  3. # psycopg/sql.py - SQL composition utility module
  4. #
  5. # Copyright (C) 2016-2019 Daniele Varrazzo <daniele.varrazzo@gmail.com>
  6. # Copyright (C) 2020-2021 The Psycopg Team
  7. #
  8. # psycopg2 is free software: you can redistribute it and/or modify it
  9. # under the terms of the GNU Lesser General Public License as published
  10. # by the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # In addition, as a special exception, the copyright holders give
  14. # permission to link this program with the OpenSSL library (or with
  15. # modified versions of OpenSSL that use the same license as OpenSSL),
  16. # and distribute linked combinations including the two.
  17. #
  18. # You must obey the GNU Lesser General Public License in all respects for
  19. # all of the code used other than OpenSSL.
  20. #
  21. # psycopg2 is distributed in the hope that it will be useful, but WITHOUT
  22. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  23. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  24. # License for more details.
  25. import string
  26. from psycopg2 import extensions as ext
  27. _formatter = string.Formatter()
  28. class Composable:
  29. """
  30. Abstract base class for objects that can be used to compose an SQL string.
  31. `!Composable` objects can be passed directly to `~cursor.execute()`,
  32. `~cursor.executemany()`, `~cursor.copy_expert()` in place of the query
  33. string.
  34. `!Composable` objects can be joined using the ``+`` operator: the result
  35. will be a `Composed` instance containing the objects joined. The operator
  36. ``*`` is also supported with an integer argument: the result is a
  37. `!Composed` instance containing the left argument repeated as many times as
  38. requested.
  39. """
  40. def __init__(self, wrapped):
  41. self._wrapped = wrapped
  42. def __repr__(self):
  43. return f"{self.__class__.__name__}({self._wrapped!r})"
  44. def as_string(self, context):
  45. """
  46. Return the string value of the object.
  47. :param context: the context to evaluate the string into.
  48. :type context: `connection` or `cursor`
  49. The method is automatically invoked by `~cursor.execute()`,
  50. `~cursor.executemany()`, `~cursor.copy_expert()` if a `!Composable` is
  51. passed instead of the query string.
  52. """
  53. raise NotImplementedError
  54. def __add__(self, other):
  55. if isinstance(other, Composed):
  56. return Composed([self]) + other
  57. if isinstance(other, Composable):
  58. return Composed([self]) + Composed([other])
  59. else:
  60. return NotImplemented
  61. def __mul__(self, n):
  62. return Composed([self] * n)
  63. def __eq__(self, other):
  64. return type(self) is type(other) and self._wrapped == other._wrapped
  65. def __ne__(self, other):
  66. return not self.__eq__(other)
  67. class Composed(Composable):
  68. """
  69. A `Composable` object made of a sequence of `!Composable`.
  70. The object is usually created using `!Composable` operators and methods.
  71. However it is possible to create a `!Composed` directly specifying a
  72. sequence of `!Composable` as arguments.
  73. Example::
  74. >>> comp = sql.Composed(
  75. ... [sql.SQL("insert into "), sql.Identifier("table")])
  76. >>> print(comp.as_string(conn))
  77. insert into "table"
  78. `!Composed` objects are iterable (so they can be used in `SQL.join` for
  79. instance).
  80. """
  81. def __init__(self, seq):
  82. wrapped = []
  83. for i in seq:
  84. if not isinstance(i, Composable):
  85. raise TypeError(
  86. f"Composed elements must be Composable, got {i!r} instead")
  87. wrapped.append(i)
  88. super().__init__(wrapped)
  89. @property
  90. def seq(self):
  91. """The list of the content of the `!Composed`."""
  92. return list(self._wrapped)
  93. def as_string(self, context):
  94. rv = []
  95. for i in self._wrapped:
  96. rv.append(i.as_string(context))
  97. return ''.join(rv)
  98. def __iter__(self):
  99. return iter(self._wrapped)
  100. def __add__(self, other):
  101. if isinstance(other, Composed):
  102. return Composed(self._wrapped + other._wrapped)
  103. if isinstance(other, Composable):
  104. return Composed(self._wrapped + [other])
  105. else:
  106. return NotImplemented
  107. def join(self, joiner):
  108. """
  109. Return a new `!Composed` interposing the *joiner* with the `!Composed` items.
  110. The *joiner* must be a `SQL` or a string which will be interpreted as
  111. an `SQL`.
  112. Example::
  113. >>> fields = sql.Identifier('foo') + sql.Identifier('bar') # a Composed
  114. >>> print(fields.join(', ').as_string(conn))
  115. "foo", "bar"
  116. """
  117. if isinstance(joiner, str):
  118. joiner = SQL(joiner)
  119. elif not isinstance(joiner, SQL):
  120. raise TypeError(
  121. "Composed.join() argument must be a string or an SQL")
  122. return joiner.join(self)
  123. class SQL(Composable):
  124. """
  125. A `Composable` representing a snippet of SQL statement.
  126. `!SQL` exposes `join()` and `format()` methods useful to create a template
  127. where to merge variable parts of a query (for instance field or table
  128. names).
  129. The *string* doesn't undergo any form of escaping, so it is not suitable to
  130. represent variable identifiers or values: you should only use it to pass
  131. constant strings representing templates or snippets of SQL statements; use
  132. other objects such as `Identifier` or `Literal` to represent variable
  133. parts.
  134. Example::
  135. >>> query = sql.SQL("select {0} from {1}").format(
  136. ... sql.SQL(', ').join([sql.Identifier('foo'), sql.Identifier('bar')]),
  137. ... sql.Identifier('table'))
  138. >>> print(query.as_string(conn))
  139. select "foo", "bar" from "table"
  140. """
  141. def __init__(self, string):
  142. if not isinstance(string, str):
  143. raise TypeError("SQL values must be strings")
  144. super().__init__(string)
  145. @property
  146. def string(self):
  147. """The string wrapped by the `!SQL` object."""
  148. return self._wrapped
  149. def as_string(self, context):
  150. return self._wrapped
  151. def format(self, *args, **kwargs):
  152. """
  153. Merge `Composable` objects into a template.
  154. :param `Composable` args: parameters to replace to numbered
  155. (``{0}``, ``{1}``) or auto-numbered (``{}``) placeholders
  156. :param `Composable` kwargs: parameters to replace to named (``{name}``)
  157. placeholders
  158. :return: the union of the `!SQL` string with placeholders replaced
  159. :rtype: `Composed`
  160. The method is similar to the Python `str.format()` method: the string
  161. template supports auto-numbered (``{}``), numbered (``{0}``,
  162. ``{1}``...), and named placeholders (``{name}``), with positional
  163. arguments replacing the numbered placeholders and keywords replacing
  164. the named ones. However placeholder modifiers (``{0!r}``, ``{0:<10}``)
  165. are not supported. Only `!Composable` objects can be passed to the
  166. template.
  167. Example::
  168. >>> print(sql.SQL("select * from {} where {} = %s")
  169. ... .format(sql.Identifier('people'), sql.Identifier('id'))
  170. ... .as_string(conn))
  171. select * from "people" where "id" = %s
  172. >>> print(sql.SQL("select * from {tbl} where {pkey} = %s")
  173. ... .format(tbl=sql.Identifier('people'), pkey=sql.Identifier('id'))
  174. ... .as_string(conn))
  175. select * from "people" where "id" = %s
  176. """
  177. rv = []
  178. autonum = 0
  179. for pre, name, spec, conv in _formatter.parse(self._wrapped):
  180. if spec:
  181. raise ValueError("no format specification supported by SQL")
  182. if conv:
  183. raise ValueError("no format conversion supported by SQL")
  184. if pre:
  185. rv.append(SQL(pre))
  186. if name is None:
  187. continue
  188. if name.isdigit():
  189. if autonum:
  190. raise ValueError(
  191. "cannot switch from automatic field numbering to manual")
  192. rv.append(args[int(name)])
  193. autonum = None
  194. elif not name:
  195. if autonum is None:
  196. raise ValueError(
  197. "cannot switch from manual field numbering to automatic")
  198. rv.append(args[autonum])
  199. autonum += 1
  200. else:
  201. rv.append(kwargs[name])
  202. return Composed(rv)
  203. def join(self, seq):
  204. """
  205. Join a sequence of `Composable`.
  206. :param seq: the elements to join.
  207. :type seq: iterable of `!Composable`
  208. Use the `!SQL` object's *string* to separate the elements in *seq*.
  209. Note that `Composed` objects are iterable too, so they can be used as
  210. argument for this method.
  211. Example::
  212. >>> snip = sql.SQL(', ').join(
  213. ... sql.Identifier(n) for n in ['foo', 'bar', 'baz'])
  214. >>> print(snip.as_string(conn))
  215. "foo", "bar", "baz"
  216. """
  217. rv = []
  218. it = iter(seq)
  219. try:
  220. rv.append(next(it))
  221. except StopIteration:
  222. pass
  223. else:
  224. for i in it:
  225. rv.append(self)
  226. rv.append(i)
  227. return Composed(rv)
  228. class Identifier(Composable):
  229. """
  230. A `Composable` representing an SQL identifier or a dot-separated sequence.
  231. Identifiers usually represent names of database objects, such as tables or
  232. fields. PostgreSQL identifiers follow `different rules`__ than SQL string
  233. literals for escaping (e.g. they use double quotes instead of single).
  234. .. __: https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html# \
  235. SQL-SYNTAX-IDENTIFIERS
  236. Example::
  237. >>> t1 = sql.Identifier("foo")
  238. >>> t2 = sql.Identifier("ba'r")
  239. >>> t3 = sql.Identifier('ba"z')
  240. >>> print(sql.SQL(', ').join([t1, t2, t3]).as_string(conn))
  241. "foo", "ba'r", "ba""z"
  242. Multiple strings can be passed to the object to represent a qualified name,
  243. i.e. a dot-separated sequence of identifiers.
  244. Example::
  245. >>> query = sql.SQL("select {} from {}").format(
  246. ... sql.Identifier("table", "field"),
  247. ... sql.Identifier("schema", "table"))
  248. >>> print(query.as_string(conn))
  249. select "table"."field" from "schema"."table"
  250. """
  251. def __init__(self, *strings):
  252. if not strings:
  253. raise TypeError("Identifier cannot be empty")
  254. for s in strings:
  255. if not isinstance(s, str):
  256. raise TypeError("SQL identifier parts must be strings")
  257. super().__init__(strings)
  258. @property
  259. def strings(self):
  260. """A tuple with the strings wrapped by the `Identifier`."""
  261. return self._wrapped
  262. @property
  263. def string(self):
  264. """The string wrapped by the `Identifier`.
  265. """
  266. if len(self._wrapped) == 1:
  267. return self._wrapped[0]
  268. else:
  269. raise AttributeError(
  270. "the Identifier wraps more than one than one string")
  271. def __repr__(self):
  272. return f"{self.__class__.__name__}({', '.join(map(repr, self._wrapped))})"
  273. def as_string(self, context):
  274. return '.'.join(ext.quote_ident(s, context) for s in self._wrapped)
  275. class Literal(Composable):
  276. """
  277. A `Composable` representing an SQL value to include in a query.
  278. Usually you will want to include placeholders in the query and pass values
  279. as `~cursor.execute()` arguments. If however you really really need to
  280. include a literal value in the query you can use this object.
  281. The string returned by `!as_string()` follows the normal :ref:`adaptation
  282. rules <python-types-adaptation>` for Python objects.
  283. Example::
  284. >>> s1 = sql.Literal("foo")
  285. >>> s2 = sql.Literal("ba'r")
  286. >>> s3 = sql.Literal(42)
  287. >>> print(sql.SQL(', ').join([s1, s2, s3]).as_string(conn))
  288. 'foo', 'ba''r', 42
  289. """
  290. @property
  291. def wrapped(self):
  292. """The object wrapped by the `!Literal`."""
  293. return self._wrapped
  294. def as_string(self, context):
  295. # is it a connection or cursor?
  296. if isinstance(context, ext.connection):
  297. conn = context
  298. elif isinstance(context, ext.cursor):
  299. conn = context.connection
  300. else:
  301. raise TypeError("context must be a connection or a cursor")
  302. a = ext.adapt(self._wrapped)
  303. if hasattr(a, 'prepare'):
  304. a.prepare(conn)
  305. rv = a.getquoted()
  306. if isinstance(rv, bytes):
  307. rv = rv.decode(ext.encodings[conn.encoding])
  308. return rv
  309. class Placeholder(Composable):
  310. """A `Composable` representing a placeholder for query parameters.
  311. If the name is specified, generate a named placeholder (e.g. ``%(name)s``),
  312. otherwise generate a positional placeholder (e.g. ``%s``).
  313. The object is useful to generate SQL queries with a variable number of
  314. arguments.
  315. Examples::
  316. >>> names = ['foo', 'bar', 'baz']
  317. >>> q1 = sql.SQL("insert into table ({}) values ({})").format(
  318. ... sql.SQL(', ').join(map(sql.Identifier, names)),
  319. ... sql.SQL(', ').join(sql.Placeholder() * len(names)))
  320. >>> print(q1.as_string(conn))
  321. insert into table ("foo", "bar", "baz") values (%s, %s, %s)
  322. >>> q2 = sql.SQL("insert into table ({}) values ({})").format(
  323. ... sql.SQL(', ').join(map(sql.Identifier, names)),
  324. ... sql.SQL(', ').join(map(sql.Placeholder, names)))
  325. >>> print(q2.as_string(conn))
  326. insert into table ("foo", "bar", "baz") values (%(foo)s, %(bar)s, %(baz)s)
  327. """
  328. def __init__(self, name=None):
  329. if isinstance(name, str):
  330. if ')' in name:
  331. raise ValueError(f"invalid name: {name!r}")
  332. elif name is not None:
  333. raise TypeError(f"expected string or None as name, got {name!r}")
  334. super().__init__(name)
  335. @property
  336. def name(self):
  337. """The name of the `!Placeholder`."""
  338. return self._wrapped
  339. def __repr__(self):
  340. if self._wrapped is None:
  341. return f"{self.__class__.__name__}()"
  342. else:
  343. return f"{self.__class__.__name__}({self._wrapped!r})"
  344. def as_string(self, context):
  345. if self._wrapped is not None:
  346. return f"%({self._wrapped})s"
  347. else:
  348. return "%s"
  349. # Literals
  350. NULL = SQL("NULL")
  351. DEFAULT = SQL("DEFAULT")