mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 06:07:34 +00:00
LibSQL: Parse and execute sequential placeholder values
This partially implements SQLite's bind-parameter expression to support indicating placeholder values in a SQL statement. For example: INSERT INTO table VALUES (42, ?); In the above statement, the '?' identifier is a placeholder. This will allow clients to compile statements a single time while running those statements any number of times with different placeholder values. Further, this will help mitigate SQL injection attacks.
This commit is contained in:
parent
53f8d62ea4
commit
b2b9ae27fd
10 changed files with 154 additions and 30 deletions
|
@ -300,7 +300,8 @@ private:
|
|||
|
||||
struct ExecutionContext {
|
||||
NonnullRefPtr<Database> database;
|
||||
class Statement const* statement;
|
||||
Statement const* statement { nullptr };
|
||||
Span<Value const> placeholder_values {};
|
||||
Tuple* current_row { nullptr };
|
||||
};
|
||||
|
||||
|
@ -361,6 +362,21 @@ public:
|
|||
virtual ResultOr<Value> evaluate(ExecutionContext&) const override;
|
||||
};
|
||||
|
||||
class Placeholder : public Expression {
|
||||
public:
|
||||
explicit Placeholder(size_t parameter_index)
|
||||
: m_parameter_index(parameter_index)
|
||||
{
|
||||
}
|
||||
|
||||
size_t parameter_index() const { return m_parameter_index; }
|
||||
|
||||
virtual ResultOr<Value> evaluate(ExecutionContext&) const override;
|
||||
|
||||
private:
|
||||
size_t m_parameter_index { 0 };
|
||||
};
|
||||
|
||||
class NestedExpression : public Expression {
|
||||
public:
|
||||
NonnullRefPtr<Expression> const& expression() const { return m_expression; }
|
||||
|
@ -729,7 +745,7 @@ private:
|
|||
|
||||
class Statement : public ASTNode {
|
||||
public:
|
||||
ResultOr<ResultSet> execute(AK::NonnullRefPtr<Database> database) const;
|
||||
ResultOr<ResultSet> execute(AK::NonnullRefPtr<Database> database, Span<Value const> placeholder_values = {}) const;
|
||||
|
||||
virtual ResultOr<ResultSet> execute(ExecutionContext&) const
|
||||
{
|
||||
|
|
|
@ -29,6 +29,13 @@ ResultOr<Value> NullLiteral::evaluate(ExecutionContext&) const
|
|||
return Value {};
|
||||
}
|
||||
|
||||
ResultOr<Value> Placeholder::evaluate(ExecutionContext& context) const
|
||||
{
|
||||
if (parameter_index() >= context.placeholder_values.size())
|
||||
return Result { SQLCommand::Unknown, SQLErrorCode::InvalidNumberOfPlaceholderValues };
|
||||
return context.placeholder_values[parameter_index()];
|
||||
}
|
||||
|
||||
ResultOr<Value> NestedExpression::evaluate(ExecutionContext& context) const
|
||||
{
|
||||
return expression()->evaluate(context);
|
||||
|
|
|
@ -401,7 +401,6 @@ NonnullRefPtr<Expression> Parser::parse_expression()
|
|||
if (match_secondary_expression())
|
||||
expression = parse_secondary_expression(move(expression));
|
||||
|
||||
// FIXME: Parse 'bind-parameter'.
|
||||
// FIXME: Parse 'function-name'.
|
||||
// FIXME: Parse 'raise-function'.
|
||||
|
||||
|
@ -414,6 +413,9 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression()
|
|||
if (auto expression = parse_literal_value_expression())
|
||||
return expression.release_nonnull();
|
||||
|
||||
if (auto expression = parse_bind_parameter_expression())
|
||||
return expression.release_nonnull();
|
||||
|
||||
if (auto expression = parse_column_name_expression())
|
||||
return expression.release_nonnull();
|
||||
|
||||
|
@ -528,6 +530,21 @@ RefPtr<Expression> Parser::parse_literal_value_expression()
|
|||
return {};
|
||||
}
|
||||
|
||||
// https://sqlite.org/lang_expr.html#varparam
|
||||
RefPtr<Expression> Parser::parse_bind_parameter_expression()
|
||||
{
|
||||
// FIXME: Support ?NNN, :AAAA, @AAAA, and $AAAA forms.
|
||||
if (consume_if(TokenType::Placeholder)) {
|
||||
auto parameter = m_parser_state.m_bound_parameters;
|
||||
if (++m_parser_state.m_bound_parameters > Limits::maximum_bound_parameters)
|
||||
syntax_error(DeprecatedString::formatted("Exceeded maximum number of bound parameters {}", Limits::maximum_bound_parameters));
|
||||
|
||||
return create_ast_node<Placeholder>(parameter);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
RefPtr<Expression> Parser::parse_column_name_expression(DeprecatedString with_parsed_identifier, bool with_parsed_period)
|
||||
{
|
||||
if (with_parsed_identifier.is_null() && !match(TokenType::Identifier))
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace Limits {
|
|||
// https://www.sqlite.org/limits.html
|
||||
constexpr size_t maximum_expression_tree_depth = 1000;
|
||||
constexpr size_t maximum_subquery_depth = 100;
|
||||
constexpr size_t maximum_bound_parameters = 1000;
|
||||
}
|
||||
|
||||
class Parser {
|
||||
|
@ -52,6 +53,7 @@ private:
|
|||
Vector<Error> m_errors;
|
||||
size_t m_current_expression_depth { 0 };
|
||||
size_t m_current_subquery_depth { 0 };
|
||||
size_t m_bound_parameters { 0 };
|
||||
};
|
||||
|
||||
NonnullRefPtr<Statement> parse_statement();
|
||||
|
@ -71,6 +73,7 @@ private:
|
|||
NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression> primary);
|
||||
bool match_secondary_expression() const;
|
||||
RefPtr<Expression> parse_literal_value_expression();
|
||||
RefPtr<Expression> parse_bind_parameter_expression();
|
||||
RefPtr<Expression> parse_column_name_expression(DeprecatedString with_parsed_identifier = {}, bool with_parsed_period = false);
|
||||
RefPtr<Expression> parse_unary_operator_expression();
|
||||
RefPtr<Expression> parse_binary_operator_expression(NonnullRefPtr<Expression> lhs);
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
namespace SQL::AST {
|
||||
|
||||
ResultOr<ResultSet> Statement::execute(AK::NonnullRefPtr<Database> database) const
|
||||
ResultOr<ResultSet> Statement::execute(AK::NonnullRefPtr<Database> database, Span<Value const> placeholder_values) const
|
||||
{
|
||||
ExecutionContext context { move(database), this, nullptr };
|
||||
ExecutionContext context { move(database), this, placeholder_values, nullptr };
|
||||
auto result = TRY(execute(context));
|
||||
|
||||
// FIXME: When transactional sessions are supported, don't auto-commit modifications.
|
||||
|
|
|
@ -171,6 +171,7 @@ namespace SQL::AST {
|
|||
__ENUMERATE_SQL_TOKEN("_blob_", BlobLiteral, Blob) \
|
||||
__ENUMERATE_SQL_TOKEN("_eof_", Eof, Invalid) \
|
||||
__ENUMERATE_SQL_TOKEN("_invalid_", Invalid, Invalid) \
|
||||
__ENUMERATE_SQL_TOKEN("?", Placeholder, Operator) \
|
||||
__ENUMERATE_SQL_TOKEN("&", Ampersand, Operator) \
|
||||
__ENUMERATE_SQL_TOKEN("*", Asterisk, Operator) \
|
||||
__ENUMERATE_SQL_TOKEN(",", Comma, Punctuation) \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue