sql.py 15 KB

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