[Tarantool-patches] [PATCH v1 1/2] sql: properly check bind variable names

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Sat Nov 20 03:45:36 MSK 2021


Thanks for the patch!

Shouldn't these be the same?

tarantool> box.execute('SELECT $name', {})
---
- null
- Index of binding slots must start from 1
...

tarantool> box.execute('SELECT @name', {})
---
- metadata:
  - name: COLUMN_1
    type: boolean
  rows:
  - [null]
...

See 4 comments below.

> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index eb169aeb8..74a98c550 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -1314,6 +1314,52 @@ sqlExprAssignVarNumber(Parse * pParse, Expr * pExpr, u32 n)
>  	}
>  }
>  
> +struct Expr *
> +expr_variable(struct Parse *parse, struct Token *spec, struct Token *id)

1. You might want to call it expr_new_variable(). Or sql_expr_new_variable().
To be consistent with our naming policy for constructors allocating memory
and for consistency with with sql_expr_new_column(), sql_expr_new(),
sql_expr_new_dequoted(), sql_expr_new_named(), sql_expr_new_anon().


2. spec and id can be made const.

> +{
> +	assert(spec != 0 && id != NULL && spec->n == 1);
> +	if (id->z - spec->z != 1) {
> +		diag_set(ClientError, ER_SQL_UNKNOWN_TOKEN, parse->line_count,
> +			 spec->z - parse->zTail + 1, spec->n, spec->z);
> +		parse->is_aborted = true;
> +		return NULL;
> +	}
> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> index 92fb37dd4..06e6244e3 100644
> --- a/src/box/sql/parse.y
> +++ b/src/box/sql/parse.y
> @@ -1087,31 +1087,29 @@ term(A) ::= INTEGER(X). {
>    A.zEnd = X.z + X.n;
>    if( A.pExpr ) A.pExpr->flags |= EP_Leaf;
>  }
> -expr(A) ::= VARIABLE(X).     {
> -  Token t = X;
> +expr(A) ::= VARNUM(X). {
>    if (pParse->parse_only) {
> -    spanSet(&A, &t, &t);
>      diag_set(ClientError, ER_SQL_PARSER_GENERIC_WITH_POS, pParse->line_count,
>               pParse->line_pos, "bindings are not allowed in DDL");
>      pParse->is_aborted = true;

3. Hm. It looks not so good that this complicated error is now in
2 places and is exactly the same. Maybe we could have a common
function both for positional and named arguments? Or 2 functions,
but they would use one more generic function inside?

For instance, one function could be

	sql_expr_new_var(struct Parse *parse, const struct Token *spec,
			 const struct Token *id);

For positionals it would be called as

	sql_expr_new_var(pParse, &X, NULL);

For named:

	sql_expr_new_var(pParse, &X, &Y); (like now)

Then we could also drop TK_VARIABLE from spanExpr() completely.

>      A.pExpr = NULL;
> -  } else if (!(X.z[0]=='#' && sqlIsdigit(X.z[1]))) {
> -    u32 n = X.n;
> +    A.zEnd = &X.z[X.n];
> +  } else {
>      spanExpr(&A, pParse, TK_VARIABLE, X);
> -    if (A.pExpr->u.zToken[0] == '?' && n > 1) {
> -      diag_set(ClientError, ER_SQL_SYNTAX_NEAR_TOKEN, pParse->line_count, t.n, t.z);
> -      pParse->is_aborted = true;
> -    } else {
> -      sqlExprAssignVarNumber(pParse, A.pExpr, n);
> -    }
> -  }else{
> -    assert( t.n>=2 );
> -    spanSet(&A, &t, &t);
> -    diag_set(ClientError, ER_SQL_SYNTAX_NEAR_TOKEN, pParse->line_count, t.n, t.z);
> -    pParse->is_aborted = true;
> -    A.pExpr = NULL;
> +    assert(A.pExpr->u.zToken[0] == '?' && X.n == 1);
> +    sqlExprAssignVarNumber(pParse, A.pExpr, X.n);
>    }
>  }
> +expr(A) ::= VARIABLE(X) id(Y).     {
> +  A.pExpr = expr_variable(pParse, &X, &Y);
> +  A.zStart = X.z;
> +  A.zEnd = &Y.z[Y.n];
> +}
> +expr(A) ::= VARIABLE(X) INTEGER(Y).     {
> +  A.pExpr = expr_variable(pParse, &X, &Y);
> +  A.zStart = X.z;
> +  A.zEnd = &Y.z[Y.n];
> +}

4. Lets drop the extra spaces before { symbols.


More information about the Tarantool-patches mailing list