render.py 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import inspect
  2. import six
  3. import sqlalchemy as sa
  4. from .mock import create_mock_engine
  5. from .orm import _get_query_compile_state
  6. def render_expression(expression, bind, stream=None):
  7. """Generate a SQL expression from the passed python expression.
  8. Only the global variable, `engine`, is available for use in the
  9. expression. Additional local variables may be passed in the context
  10. parameter.
  11. Note this function is meant for convenience and protected usage. Do NOT
  12. blindly pass user input to this function as it uses exec.
  13. :param bind: A SQLAlchemy engine or bind URL.
  14. :param stream: Render all DDL operations to the stream.
  15. """
  16. # Create a stream if not present.
  17. if stream is None:
  18. stream = six.moves.cStringIO()
  19. engine = create_mock_engine(bind, stream)
  20. # Navigate the stack and find the calling frame that allows the
  21. # expression to execuate.
  22. for frame in inspect.stack()[1:]:
  23. try:
  24. frame = frame[0]
  25. local = dict(frame.f_locals)
  26. local['engine'] = engine
  27. six.exec_(expression, frame.f_globals, local)
  28. break
  29. except Exception:
  30. pass
  31. else:
  32. raise ValueError('Not a valid python expression', engine)
  33. return stream
  34. def render_statement(statement, bind=None):
  35. """
  36. Generate an SQL expression string with bound parameters rendered inline
  37. for the given SQLAlchemy statement.
  38. :param statement: SQLAlchemy Query object.
  39. :param bind:
  40. Optional SQLAlchemy bind, if None uses the bind of the given query
  41. object.
  42. """
  43. if isinstance(statement, sa.orm.query.Query):
  44. if bind is None:
  45. bind = statement.session.get_bind(
  46. _get_query_compile_state(statement)._mapper_zero()
  47. )
  48. statement = statement.statement
  49. elif bind is None:
  50. bind = statement.bind
  51. stream = six.moves.cStringIO()
  52. engine = create_mock_engine(bind.engine, stream=stream)
  53. engine.execute(statement)
  54. return stream.getvalue()