mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Python: Rewrite most of SQLAlchemy modeling
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the 'SqlAlchemy' package.
|
||||
* See https://pypi.org/project/SQLAlchemy/.
|
||||
* Provides classes modeling security-relevant aspects of the `SQLAlchemy` PyPI package.
|
||||
* See
|
||||
* - https://pypi.org/project/SQLAlchemy/
|
||||
* - https://docs.sqlalchemy.org/en/14/index.html
|
||||
*/
|
||||
|
||||
private import python
|
||||
@@ -10,93 +12,209 @@ private import semmle.python.ApiGraphs
|
||||
private import semmle.python.Concepts
|
||||
private import experimental.semmle.python.Concepts
|
||||
|
||||
/**
|
||||
* Provides models for the `SQLAlchemy` PyPI package.
|
||||
* See
|
||||
* - https://pypi.org/project/SQLAlchemy/
|
||||
* - https://docs.sqlalchemy.org/en/14/index.html
|
||||
*/
|
||||
private module SqlAlchemy {
|
||||
/**
|
||||
* Returns an instantization of a SqlAlchemy Session object.
|
||||
* See https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.Session and
|
||||
* https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.sessionmaker
|
||||
*/
|
||||
private API::Node getSqlAlchemySessionInstance() {
|
||||
result = API::moduleImport("sqlalchemy.orm").getMember("Session").getReturn() or
|
||||
result = API::moduleImport("sqlalchemy.orm").getMember("sessionmaker").getReturn().getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instantization of a SqlAlchemy Engine object.
|
||||
* See https://docs.sqlalchemy.org/en/14/core/engines.html#sqlalchemy.create_engine
|
||||
*/
|
||||
private API::Node getSqlAlchemyEngineInstance() {
|
||||
result = API::moduleImport("sqlalchemy").getMember("create_engine").getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instantization of a SqlAlchemy Query object.
|
||||
* See https://docs.sqlalchemy.org/en/14/orm/query.html?highlight=query#sqlalchemy.orm.Query
|
||||
*/
|
||||
private API::Node getSqlAlchemyQueryInstance() {
|
||||
result = getSqlAlchemySessionInstance().getMember("query").getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execute` meant to execute an SQL expression
|
||||
* See the following links:
|
||||
* - https://docs.sqlalchemy.org/en/14/core/connections.html?highlight=execute#sqlalchemy.engine.Connection.execute
|
||||
* - https://docs.sqlalchemy.org/en/14/core/connections.html?highlight=execute#sqlalchemy.engine.Engine.execute
|
||||
* - https://docs.sqlalchemy.org/en/14/orm/session_api.html?highlight=execute#sqlalchemy.orm.Session.execute
|
||||
*/
|
||||
private class SqlAlchemyExecuteCall extends DataFlow::CallCfgNode, SqlExecution::Range {
|
||||
SqlAlchemyExecuteCall() {
|
||||
// new way
|
||||
this = getSqlAlchemySessionInstance().getMember("execute").getACall() or
|
||||
this =
|
||||
getSqlAlchemyEngineInstance()
|
||||
.getMember(["connect", "begin"])
|
||||
.getReturn()
|
||||
.getMember("execute")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getArg(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `scalar` meant to execute an SQL expression
|
||||
* See https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.Session.scalar and
|
||||
* https://docs.sqlalchemy.org/en/14/core/connections.html?highlight=scalar#sqlalchemy.engine.Engine.scalar
|
||||
*/
|
||||
private class SqlAlchemyScalarCall extends DataFlow::CallCfgNode, SqlExecution::Range {
|
||||
SqlAlchemyScalarCall() {
|
||||
this =
|
||||
[getSqlAlchemySessionInstance(), getSqlAlchemyEngineInstance()]
|
||||
.getMember("scalar")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getArg(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call on a Query object
|
||||
* See https://docs.sqlalchemy.org/en/14/orm/query.html?highlight=query#sqlalchemy.orm.Query
|
||||
*/
|
||||
private class SqlAlchemyQueryCall extends DataFlow::CallCfgNode, SqlExecution::Range {
|
||||
SqlAlchemyQueryCall() {
|
||||
this =
|
||||
getSqlAlchemyQueryInstance()
|
||||
.getMember(any(SqlAlchemyVulnerableMethodNames methodName))
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getArg(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents a list of methods vulnerable to sql injection.
|
||||
* Provides models for the `sqlalchemy.engine.Engine` and `sqlalchemy.future.Engine` classes.
|
||||
*
|
||||
* See https://github.com/jty-team/codeql/pull/2#issue-611592361
|
||||
* These are so similar that we model both in the same way.
|
||||
*
|
||||
* See
|
||||
* - https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Engine
|
||||
* - https://docs.sqlalchemy.org/en/14/core/future.html#sqlalchemy.future.Engine
|
||||
*/
|
||||
private class SqlAlchemyVulnerableMethodNames extends string {
|
||||
SqlAlchemyVulnerableMethodNames() { this in ["filter", "filter_by", "group_by", "order_by"] }
|
||||
module Engine {
|
||||
/** Gets a reference to a SQLAlchemy Engine class. */
|
||||
private API::Node classRef() {
|
||||
result = API::moduleImport("sqlalchemy").getMember("engine").getMember("Engine")
|
||||
or
|
||||
result = API::moduleImport("sqlalchemy").getMember("future").getMember("Engine")
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of instances of a SQLAlchemy Engine, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `Engine::instance()` to get references to instances of a SQLAlchemy Engine.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
private class EngineConstruction extends InstanceSource, DataFlow::CallCfgNode {
|
||||
EngineConstruction() {
|
||||
this = classRef().getACall()
|
||||
or
|
||||
this = API::moduleImport("sqlalchemy").getMember("create_engine").getACall()
|
||||
or
|
||||
this =
|
||||
API::moduleImport("sqlalchemy").getMember("future").getMember("create_engine").getACall()
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(instance(), "execution_options")
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a SQLAlchemy Engine. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a SQLAlchemy Engine. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `sqlalchemy.engine.base.Connection` and `sqlalchemy.future.Connection` classes.
|
||||
*
|
||||
* These are so similar that we model both in the same way.
|
||||
*
|
||||
* See
|
||||
* - https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Connection
|
||||
* - https://docs.sqlalchemy.org/en/14/core/future.html#sqlalchemy.future.Connection
|
||||
*/
|
||||
module Connection {
|
||||
/** Gets a reference to a SQLAlchemy Connection class. */
|
||||
private API::Node classRef() {
|
||||
result =
|
||||
API::moduleImport("sqlalchemy")
|
||||
.getMember("engine")
|
||||
.getMember("base")
|
||||
.getMember("Connection")
|
||||
or
|
||||
result = API::moduleImport("sqlalchemy").getMember("future").getMember("Connection")
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of instances of a SQLAlchemy Connection, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `Connection::instance()` to get references to instances of a SQLAlchemy Connection.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
private class ConnectionConstruction extends InstanceSource, DataFlow::CallCfgNode {
|
||||
ConnectionConstruction() {
|
||||
this = classRef().getACall()
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(Engine::instance(), ["begin", "connect"])
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(instance(), "connect")
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a SQLAlchemy Connection. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a SQLAlchemy Connection. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `sqlalchemy.orm.Session` class
|
||||
*
|
||||
* See
|
||||
* - https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.Session
|
||||
* - https://docs.sqlalchemy.org/en/14/orm/session_basics.html
|
||||
*/
|
||||
module Session {
|
||||
/** Gets a reference to the `sqlalchemy.orm.Session` class. */
|
||||
private API::Node classRef() {
|
||||
result = API::moduleImport("sqlalchemy").getMember("orm").getMember("Session")
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of instances of `sqlalchemy.orm.Session`, extend this class to model new instances.
|
||||
*
|
||||
* This can include instantiations of the class, return values from function
|
||||
* calls, or a special parameter that will be set when functions are called by an external
|
||||
* library.
|
||||
*
|
||||
* Use the predicate `Session::instance()` to get references to instances of `sqlalchemy.orm.Session`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
private class SessionConstruction extends InstanceSource, DataFlow::CallCfgNode {
|
||||
SessionConstruction() {
|
||||
this = classRef().getACall()
|
||||
or
|
||||
this =
|
||||
API::moduleImport("sqlalchemy")
|
||||
.getMember("orm")
|
||||
.getMember("sessionmaker")
|
||||
.getReturn()
|
||||
.getACall()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `sqlalchemy.orm.Session`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `sqlalchemy.orm.Session`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execute` on a SQLAlchemy Engine, Connection, or Session.
|
||||
* See
|
||||
* - https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Engine.execute
|
||||
* - https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Connection.execute
|
||||
* - https://docs.sqlalchemy.org/en/14/core/future.html#sqlalchemy.future.Connection.execute
|
||||
* - https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.Session.execute
|
||||
*/
|
||||
private class SqlAlchemyExecuteCall extends DataFlow::MethodCallNode, SqlExecution::Range {
|
||||
SqlAlchemyExecuteCall() {
|
||||
this.calls(Engine::instance(), "execute")
|
||||
or
|
||||
this.calls(Connection::instance(), "execute")
|
||||
or
|
||||
this.calls(Session::instance(), "execute")
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("statement")] }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `scalar` on a SQLAlchemy Engine, Connection, or Session.
|
||||
* See
|
||||
* - https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Engine.scalar
|
||||
* - https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Connection.scalar
|
||||
* - https://docs.sqlalchemy.org/en/14/core/future.html#sqlalchemy.future.Connection.scalar
|
||||
* - https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.Session.scalar
|
||||
*/
|
||||
private class SqlAlchemyScalarCall extends DataFlow::MethodCallNode, SqlExecution::Range {
|
||||
SqlAlchemyScalarCall() {
|
||||
this.calls(Engine::instance(), "scalar")
|
||||
or
|
||||
this.calls(Connection::instance(), "scalar")
|
||||
or
|
||||
this.calls(Session::instance(), "scalar")
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() {
|
||||
result in [this.getArg(0), this.getArgByName("statement"), this.getArgByName("object_")]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,3 +264,47 @@ private module SqlAlchemy {
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
}
|
||||
}
|
||||
|
||||
private module OldModeling {
|
||||
/**
|
||||
* Returns an instantization of a SqlAlchemy Session object.
|
||||
* See https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.Session and
|
||||
* https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.sessionmaker
|
||||
*/
|
||||
private API::Node getSqlAlchemySessionInstance() {
|
||||
result = API::moduleImport("sqlalchemy.orm").getMember("Session").getReturn() or
|
||||
result = API::moduleImport("sqlalchemy.orm").getMember("sessionmaker").getReturn().getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instantization of a SqlAlchemy Query object.
|
||||
* See https://docs.sqlalchemy.org/en/14/orm/query.html?highlight=query#sqlalchemy.orm.Query
|
||||
*/
|
||||
private API::Node getSqlAlchemyQueryInstance() {
|
||||
result = getSqlAlchemySessionInstance().getMember("query").getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call on a Query object
|
||||
* See https://docs.sqlalchemy.org/en/14/orm/query.html?highlight=query#sqlalchemy.orm.Query
|
||||
*/
|
||||
private class SqlAlchemyQueryCall extends DataFlow::CallCfgNode, SqlExecution::Range {
|
||||
SqlAlchemyQueryCall() {
|
||||
this =
|
||||
getSqlAlchemyQueryInstance()
|
||||
.getMember(any(SqlAlchemyVulnerableMethodNames methodName))
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getArg(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents a list of methods vulnerable to sql injection.
|
||||
*
|
||||
* See https://github.com/jty-team/codeql/pull/2#issue-611592361
|
||||
*/
|
||||
private class SqlAlchemyVulnerableMethodNames extends string {
|
||||
SqlAlchemyVulnerableMethodNames() { this in ["filter", "filter_by", "group_by", "order_by"] }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,26 +20,26 @@ print("v1.4")
|
||||
|
||||
engine = sqlalchemy.create_engine("sqlite+pysqlite:///:memory:", echo=True)
|
||||
|
||||
result = engine.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = engine.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = engine.execute(statement=raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = engine.execute(statement=raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = engine.execute(text_sql) # $ MISSING: getSql=text_sql
|
||||
result = engine.execute(text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
scalar_result = engine.scalar(raw_sql) # $ getSql=raw_sql
|
||||
assert scalar_result == "FOO"
|
||||
scalar_result = engine.scalar(statement=raw_sql) # $ MISSING: getSql=raw_sql
|
||||
scalar_result = engine.scalar(statement=raw_sql) # $ getSql=raw_sql
|
||||
assert scalar_result == "FOO"
|
||||
|
||||
# engine with custom execution options
|
||||
# see https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Engine.execution_options
|
||||
engine_with_custom_exe_opts = engine.execution_options(foo=42)
|
||||
result = engine_with_custom_exe_opts.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = engine_with_custom_exe_opts.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
even_more_opts = engine_with_custom_exe_opts.execution_options(bar=43)
|
||||
result = even_more_opts.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = even_more_opts.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# Connection see https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Connection
|
||||
@@ -48,23 +48,23 @@ conn: sqlalchemy.engine.base.Connection
|
||||
|
||||
result = conn.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = conn.execute(statement=raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = conn.execute(statement=raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
result = conn.execute(text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = conn.execute(statement=text_sql) # $ MISSING: getSql=text_sql
|
||||
result = conn.execute(statement=text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# scalar
|
||||
scalar_result = conn.scalar(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
scalar_result = conn.scalar(raw_sql) # $ getSql=raw_sql
|
||||
assert scalar_result == "FOO"
|
||||
scalar_result = conn.scalar(object_=raw_sql) # $ MISSING: getSql=raw_sql
|
||||
scalar_result = conn.scalar(object_=raw_sql) # $ getSql=raw_sql
|
||||
assert scalar_result == "FOO"
|
||||
|
||||
scalar_result = conn.scalar(text_sql) # $ MISSING: getSql=text_sql
|
||||
scalar_result = conn.scalar(text_sql) # $ getSql=text_sql
|
||||
assert scalar_result == "FOO"
|
||||
scalar_result = conn.scalar(object_=text_sql) # $ MISSING: getSql=text_sql
|
||||
scalar_result = conn.scalar(object_=text_sql) # $ getSql=text_sql
|
||||
assert scalar_result == "FOO"
|
||||
|
||||
|
||||
@@ -74,12 +74,12 @@ assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# construction by object
|
||||
conn = sqlalchemy.engine.base.Connection(engine)
|
||||
result = conn.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = conn.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# branched connection
|
||||
branched_conn = conn.connect()
|
||||
result = branched_conn.execute(text_sql) # $ MISSING: getSql=text_sql
|
||||
result = branched_conn.execute(text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# raw connection
|
||||
@@ -107,40 +107,40 @@ assert result.fetchall() == [("FOO",)]
|
||||
|
||||
session = sqlalchemy.orm.Session(engine)
|
||||
|
||||
result = session.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = session.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = session.execute(statement=raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = session.execute(statement=raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
result = session.execute(text_sql) # $ MISSING: getSql=text_sql
|
||||
result = session.execute(text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = session.execute(statement=text_sql) # $ MISSING: getSql=text_sql
|
||||
result = session.execute(statement=text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# scalar
|
||||
scalar_result = session.scalar(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
scalar_result = session.scalar(raw_sql) # $ getSql=raw_sql
|
||||
assert scalar_result == "FOO"
|
||||
scalar_result = session.scalar(statement=raw_sql) # $ MISSING: getSql=raw_sql
|
||||
scalar_result = session.scalar(statement=raw_sql) # $ getSql=raw_sql
|
||||
assert scalar_result == "FOO"
|
||||
|
||||
scalar_result = session.scalar(text_sql) # $ MISSING: getSql=text_sql
|
||||
scalar_result = session.scalar(text_sql) # $ getSql=text_sql
|
||||
assert scalar_result == "FOO"
|
||||
scalar_result = session.scalar(statement=text_sql) # $ MISSING: getSql=text_sql
|
||||
scalar_result = session.scalar(statement=text_sql) # $ getSql=text_sql
|
||||
assert scalar_result == "FOO"
|
||||
|
||||
# other ways to construct a session
|
||||
with sqlalchemy.orm.Session(engine) as session:
|
||||
result = session.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = session.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
Session = sqlalchemy.orm.sessionmaker(engine)
|
||||
session = Session()
|
||||
|
||||
result = session.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = session.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
with Session() as session:
|
||||
result = session.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = session.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
with Session.begin() as session:
|
||||
@@ -255,12 +255,12 @@ future_engine = sqlalchemy.future.create_engine("sqlite+pysqlite:///:memory:", e
|
||||
|
||||
# in 2.0 you are not allowed to execute things directly on the engine
|
||||
try:
|
||||
engine.execute(raw_sql)
|
||||
engine.execute(raw_sql) # $ SPURIOUS: getSql=raw_sql
|
||||
raise Exception("above not allowed in 2.0")
|
||||
except NotImplementedError:
|
||||
pass
|
||||
try:
|
||||
engine.execute(text_sql)
|
||||
engine.execute(text_sql) # $ SPURIOUS: getSql=text_sql
|
||||
raise Exception("above not allowed in 2.0")
|
||||
except NotImplementedError:
|
||||
pass
|
||||
@@ -281,7 +281,7 @@ with engine.connect() as conn:
|
||||
|
||||
result = conn.execute(text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = conn.execute(statement=text_sql) # $ MISSING: getSql=text_sql
|
||||
result = conn.execute(statement=text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
result = conn.exec_driver_sql(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
@@ -305,12 +305,12 @@ with engine.connect() as conn:
|
||||
|
||||
# `scalar` is shorthand helper
|
||||
try:
|
||||
conn.scalar(raw_sql)
|
||||
conn.scalar(raw_sql) # $ SPURIOUS: getSql=raw_sql
|
||||
except sqlalchemy.exc.ObjectNotExecutableError:
|
||||
pass
|
||||
scalar_result = conn.scalar(text_sql) # $ MISSING: getSql=text_sql
|
||||
scalar_result = conn.scalar(text_sql) # $ getSql=text_sql
|
||||
assert scalar_result == "FOO"
|
||||
scalar_result = conn.scalar(statement=text_sql) # $ MISSING: getSql=text_sql
|
||||
scalar_result = conn.scalar(statement=text_sql) # $ getSql=text_sql
|
||||
assert scalar_result == "FOO"
|
||||
|
||||
# This is a contrived example
|
||||
@@ -324,7 +324,7 @@ with engine.connect() as conn:
|
||||
assert result.fetchall() == [("BAZ",)]
|
||||
|
||||
with future_engine.connect() as conn:
|
||||
result = conn.execute(text_sql) # $ MISSING: getSql=text_sql
|
||||
result = conn.execute(text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# `begin` returns a new Connection object with a transaction begun.
|
||||
@@ -335,7 +335,7 @@ with engine.begin() as conn:
|
||||
|
||||
# construction by object
|
||||
conn = sqlalchemy.future.Connection(engine)
|
||||
result = conn.execute(text_sql) # $ MISSING: getSql=text_sql
|
||||
result = conn.execute(text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# raw_connection
|
||||
@@ -352,25 +352,25 @@ cursor.close()
|
||||
# Session (2.0)
|
||||
session = sqlalchemy.orm.Session(engine, future=True)
|
||||
|
||||
result = session.execute(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = session.execute(raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = session.execute(statement=raw_sql) # $ MISSING: getSql=raw_sql
|
||||
result = session.execute(statement=raw_sql) # $ getSql=raw_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
result = session.execute(text_sql) # $ MISSING: getSql=text_sql
|
||||
result = session.execute(text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
result = session.execute(statement=text_sql) # $ MISSING: getSql=text_sql
|
||||
result = session.execute(statement=text_sql) # $ getSql=text_sql
|
||||
assert result.fetchall() == [("FOO",)]
|
||||
|
||||
# scalar
|
||||
scalar_result = session.scalar(raw_sql) # $ MISSING: getSql=raw_sql
|
||||
scalar_result = session.scalar(raw_sql) # $ getSql=raw_sql
|
||||
assert scalar_result == "FOO"
|
||||
scalar_result = session.scalar(statement=raw_sql) # $ MISSING: getSql=raw_sql
|
||||
scalar_result = session.scalar(statement=raw_sql) # $ getSql=raw_sql
|
||||
assert scalar_result == "FOO"
|
||||
|
||||
scalar_result = session.scalar(text_sql) # $ MISSING: getSql=text_sql
|
||||
scalar_result = session.scalar(text_sql) # $ getSql=text_sql
|
||||
assert scalar_result == "FOO"
|
||||
scalar_result = session.scalar(statement=text_sql) # $ MISSING: getSql=text_sql
|
||||
scalar_result = session.scalar(statement=text_sql) # $ getSql=text_sql
|
||||
assert scalar_result == "FOO"
|
||||
|
||||
# Querying (2.0)
|
||||
@@ -394,9 +394,9 @@ assert session.query(For20).all()[0].id == 20
|
||||
# see https://docs.sqlalchemy.org/en/14/orm/session_basics.html#querying-2-0-style
|
||||
|
||||
statement = sqlalchemy.select(For20)
|
||||
result = session.execute(statement)
|
||||
result = session.execute(statement) # $ getSql=statement
|
||||
assert result.scalars().all()[0].id == 20
|
||||
|
||||
statement = sqlalchemy.select(For20).where(For20.description == text_foo) # $ MISSING: getSql=text_foo
|
||||
result = session.execute(statement)
|
||||
result = session.execute(statement) # $ getSql=statement
|
||||
assert result.scalars().all() == []
|
||||
|
||||
Reference in New Issue
Block a user