Tarantool development patches archive
 help / color / mirror / Atom feed
From: "n.pettik" <korablev@tarantool.org>
To: tarantool-patches@freelists.org
Cc: Kirill Shcherbatov <kshcherbatov@tarantool.org>
Subject: [tarantool-patches] Re: [PATCH v2 9/9] sql: run check constraint tests on space alter
Date: Tue, 2 Apr 2019 17:14:04 +0300	[thread overview]
Message-ID: <121C5F34-691B-4FCC-A1C9-C995683D4A6A@tarantool.org> (raw)
In-Reply-To: <383ecdc1-35d8-7a15-fe7c-e1cdba941dfd@tarantool.org>



> On 26 Mar 2019, at 13:59, Kirill Shcherbatov <kshcherbatov@tarantool.org> wrote:
> 
> Introduced reusable pre-compiled VDBE programs for ck constraints
> and space trigger to fire checks on insert and update operations.

As usual, extremely brief description of vital feature.
Fill commit message with details of chosen approach.

> Closes #3691
> 
> @TarantoolBot document
> Title: check constraint for LUA space
> 
> Now it is possible to create check constraints for LUA spaces:
> insert the tuple {<CONSTRAINT NAME>, <DST SPACE ID>, <EXPRESSION STRING>} in
> the _ck_constraint space to create new check constraint.
> 
> Example:
> s = box.schema.create_space('test')
> s:create_index('pk')
> format = {{'X', 'unsigned'}, {'Y', 'unsigned'}}
> s:format(format)

Mention that format is required condition to use check
constraints: otherwise, name of field can’t be resolved.
Now it leads to assertion fault:

s = box.schema.create_space('test’)
s:create_index('pk’)
box.space._ck_constraint:insert({'physics', s.id, 'X<Y’})
Assertion failed: (space_def->field_count > 0), function lookupName, file tarantool/src/box/sql/resolve.c, line 242.

> box.space._ck_constraint:insert({'physics', s.id, 'X<Y'})
> box.space.test:insert({6, 5})
> - error: 'Check constraint failed: physics’

To finish this patch-set I suggest to add Lua-wrapper to create
check constraints on any space using NoSQL interface and introduce
ALTER TABLE ADD CONSTRAINT CHECK().
Last issue you can implement in a separate patch or delegate its
implementation to smb else.

> ---
> src/box/alter.cc         |  21 +++++-
> src/box/ck_constraint.c  | 142 ++++++++++++++++++++++++++++++++++++++-
> src/box/ck_constraint.h  |  24 +++++--
> src/box/sql.c            |   1 +
> src/box/sql/insert.c     |  94 ++++++--------------------
> src/box/sql/sqlInt.h     |  25 +++++++
> src/box/sql/vdbeapi.c    |   8 ---
> test/sql/checks.result   |  20 ++++++
> test/sql/checks.test.lua |   5 ++
> 9 files changed, 248 insertions(+), 92 deletions(-)
> 
> diff --git a/src/box/alter.cc b/src/box/alter.cc
> index 9aa5e3653..b6d1c8537 100644
> --- a/src/box/alter.cc
> +++ b/src/box/alter.cc
> @@ -1379,6 +1379,11 @@ BuildCkConstraints::alter(struct alter_space *alter)
> {
> 	rlist_swap(&alter->new_space->ck_constraint, &ck_constraint);
> 	rlist_swap(&ck_constraint, &alter->old_space->ck_constraint);
> +	struct ck_constraint *ck;
> +	rlist_foreach_entry(ck, &ck_constraint, link)
> +		trigger_clear(&ck->trigger);
> +	rlist_foreach_entry(ck, &alter->new_space->ck_constraint, link)
> +		trigger_add(&alter->new_space->before_replace, &ck->trigger);
> }
> 
> void
> @@ -1386,6 +1391,11 @@ BuildCkConstraints::rollback(struct alter_space *alter)
> {
> 	rlist_swap(&alter->old_space->ck_constraint, &ck_constraint);
> 	rlist_swap(&ck_constraint, &alter->new_space->ck_constraint);
> +	struct ck_constraint *ck;
> +	rlist_foreach_entry(ck, &ck_constraint, link)
> +		trigger_clear(&ck->trigger);
> +	rlist_foreach_entry(ck, &alter->old_space->ck_constraint, link)
> +		trigger_add(&alter->old_space->before_replace, &ck->trigger);
> }
> 
> BuildCkConstraints::~BuildCkConstraints()
> 
> diff --git a/src/box/ck_constraint.c b/src/box/ck_constraint.c
> index 110098efc..c2e8547f1 100644
> --- a/src/box/ck_constraint.c
> +++ b/src/box/ck_constraint.c
> @@ -29,11 +29,16 @@
>  * SUCH DAMAGE.
>  */
> #include <assert.h>
> +#include "bind.h"
> #include "ck_constraint.h"
> #include "errcode.h"
> +#include "session.h"
> +#include "schema.h"
> #include "small/rlist.h"
> +#include "tuple.h"
> #include "sql.h"
> #include "sql/sqlInt.h"
> +#include "sql/vdbeInt.h"
> 
> /**
>  * Resolve space_def references for check constraint via AST
> @@ -84,6 +89,130 @@ ck_constraint_def_create(struct ck_constraint_def *ck_constraint_def,
> 	rlist_create(&ck_constraint_def->link);
> }
> 
> +/**
> + * Compile constraint check subroutine.
> + * @param ck_constraint Check constraint to compile.
> + * @param expr Check constraint expression AST is built for
> + *             ck_constraint->def.

Can’t understand this description.

> + * @param space_def The space definition of the space this check
> + *                  constraint is constructed for.
> + * @retval not NULL sql_stmt program pointer on success.
> + * @retval NULL otherwise.
> + */
> +int

This function is used only in ck_constraint.c, let’s make it static.

> +ck_constraint_test_compile(struct ck_constraint *ck_constraint,

I would call it “ck_constraint_program_compile” or “ck_constraint_routine_compile”
or “ck_constraint_bytecode_compile”.

> +			   struct Expr *expr, const struct space_def *space_def)
> +{
> +	int rc = -1;

Remove this rc pls. Diff:

diff --git a/src/box/ck_constraint.c b/src/box/ck_constraint.c
index c2e8547f1..6f54e35e1 100644
--- a/src/box/ck_constraint.c
+++ b/src/box/ck_constraint.c
@@ -103,15 +103,13 @@ int
 ck_constraint_test_compile(struct ck_constraint *ck_constraint,
                           struct Expr *expr, const struct space_def *space_def)
 {
-       int rc = -1;
        assert(ck_constraint->space_id == space_def->id);
        struct Parse parser;
        sql_parser_create(&parser, sql_get());
        struct Vdbe *v = sqlGetVdbe(&parser);
        if (v == NULL) {
-               diag_set(OutOfMemory, sizeof(struct Vdbe),
-                        "sqlGetVdbe", "vdbe");
-               goto end;
+               diag_set(OutOfMemory, sizeof(struct Vdbe), "sqlDbMalloc", "v");
+               return -1;
        }
 
        /* Compile VDBE with default sql parameters. */
@@ -138,16 +136,14 @@ ck_constraint_test_compile(struct ck_constraint *ck_constraint,
                diag_set(ClientError, ER_CREATE_CK_CONSTRAINT,
                         ck_constraint->def->name,
                         "can not compile expression");
-               goto end;
+               return -1;
        }
        sql_parser_destroy(&parser);
 
        /* Restore original sql flags for user_session.  */
        user_session->sql_flags = sql_flags;
        ck_constraint->stmt = (struct sql_stmt *)v;
-       rc = 0;
-end:
-       return rc;
+       return 0;
 }

> +	assert(ck_constraint->space_id == space_def->id);
> +	struct Parse parser;
> +	sql_parser_create(&parser, sql_get());
> +	struct Vdbe *v = sqlGetVdbe(&parser);
> +	if (v == NULL) {
> +		diag_set(OutOfMemory, sizeof(struct Vdbe),
> +			 "sqlGetVdbe", "vdbe");
> +		goto end;
> +	}
> +
> +	/* Compile VDBE with default sql parameters. */
> +	struct session *user_session = current_session();
> +	uint32_t sql_flags = user_session->sql_flags;
> +	user_session->sql_flags = default_flags;
> +
> +	/*
> +	 * Generate a prologue code to bind variable new_tuple_var
> +	 * to new_tuple_reg.
> +	 */

This comment explains nothing.

> +	uint32_t field_count = space_def->field_count;
> +	int new_tuple_reg = sqlGetTempRange(&parser, field_count);
> +	struct Expr bind = {.op = TK_VARIABLE, .u.zToken = "?"};
> +	ck_constraint->new_tuple_var = parser.nVar + 1;
> +	for (uint32_t i = 0; i < field_count; i++) {
> +		sqlExprAssignVarNumber(&parser, &bind, 1);
> +		sqlExprCodeTarget(&parser, &bind, new_tuple_reg + i);
> +	}
> +	vdbe_emit_ck_constraint(&parser, expr, ck_constraint->def->name,
> +				new_tuple_reg);
> +	sql_finish_coding(&parser);

Do we need to call this function at all?

> +	if (parser.is_aborted) {
> +		diag_set(ClientError, ER_CREATE_CK_CONSTRAINT,
> +			 ck_constraint->def->name,
> +			 "can not compile expression”);

This error will re-set original parsing error. I suggest to
concatenate them.

> +/**
> + * Perform ck constraint checks with new tuple data new_tuple_raw
> + * before insert or replace in space space_def.

These variable names don’t say anything.

> + * @param ck_constraint Check constraint to test.

"To perform" or "to execute”.

> + * @param space_def The space definition of the space this check
> + *                  constraint is constructed for.
> + * @param new_tuple_raw The tuple to be inserted in space.
> + * @retval 0 if check constraint test is passed, -1 otherwise.
> + */
> +static int
> +ck_constraint_test(struct ck_constraint *ck_constraint,
> +		   struct space_def *space_def, const char *new_tuple_raw)

-> ck_constraint_execute/ck_constraint_run/ck_constraint_fire etc.

> +{
> +	assert(new_tuple_raw != NULL);
> +	/*
> +	 * Prepare parameters for checks->stmt execution:
> +	 * Unpacked new tuple fields mapped to Vdbe memory from
> +	 * variables from range:
> +	 * [new_tuple_var,new_tuple_var+field_count]
> +	 */
> +	mp_decode_array(&new_tuple_raw);
> +	/* Reset VDBE to make new bindings. */
> +	sql_reset(ck_constraint->stmt);

This function returns error code, don’t ignore it.

> +	for (uint32_t i = 0; i < space_def->field_count; i++) {
> +		struct sql_bind bind;
> +		if (sql_bind_decode(&bind, ck_constraint->new_tuple_var + i,
> +				    &new_tuple_raw) != 0)
> +			return -1;
> +		if (sql_bind_column(ck_constraint->stmt, &bind,
> +				    ck_constraint->new_tuple_var + i) != 0)
> +			return -1;
> +	}
> +	/* Checks VDBE can't expire, reset expired flag & Burn. */

What does mean “& Burn” ? Not “Checks VDBE” but
“bytecode implementing check constraint”.

> +	struct Vdbe *v = (struct Vdbe *)ck_constraint->stmt;
> +	v->expired = 0;
> +	int rc;
> +	while ((rc = sql_step(ck_constraint->stmt)) == SQL_ROW) {}
> +	if (v->rc != SQL_DONE && v->rc != SQL_TARANTOOL_ERROR)
> +		diag_set(ClientError, ER_SQL, v->zErrMsg);
> +	return rc == SQL_DONE ? 0 : -1;
> +}
> +
> +/**
> + * Trigger routine executing ck constraint check on space
> + * insert and replace.

Sounds like a set of random words :)) Re-phrase somehow pls.

> +static void
> +ck_constraint_space_trigger(struct trigger *trigger, void *event)

ck_constraint_before_replace_trigger would sound better IMHO.

> +{
> +	struct ck_constraint *ck_constraint =
> +		(struct ck_constraint *)trigger->data;
> +	struct space *space = space_by_id(ck_constraint->space_id);
> +	assert(space != NULL);
> +	struct txn *txn = (struct txn *) event;
> +	struct txn_stmt *stmt = txn_current_stmt(txn);
> +	struct tuple *new_tuple = stmt->new_tuple;
> +	if (stmt == NULL || new_tuple == NULL)
> +		return;
> +	if (ck_constraint_test(ck_constraint, space->def,
> +			       tuple_data(new_tuple)) != 0)
> +		diag_raise();
> +}
> +
> struct ck_constraint *
> ck_constraint_new(const struct ck_constraint_def *ck_constraint_def,
> 		  struct space_def *space_def)
> @@ -110,6 +239,8 @@ ck_constraint_new(const struct ck_constraint_def *ck_constraint_def,
> 	ck_constraint_def_create(ck_constraint->def, ck_constraint_def->name,
> 				 ck_constraint_name_len,
> 				 ck_constraint_def->expr_str, expr_str_len);
> +	trigger_create(&ck_constraint->trigger, ck_constraint_space_trigger,
> +		       ck_constraint, NULL);
> 	struct Expr *expr =
> 		sql_expr_compile(sql_get(), ck_constraint_def->expr_str,
> 				 expr_str_len);
> @@ -120,18 +251,23 @@ ck_constraint_new(const struct ck_constraint_def *ck_constraint_def,
> 			 box_error_message(box_error_last()));
> 		goto error;
> 	}
> -	ck_constraint->expr = expr;
> +	if (ck_constraint_test_compile(ck_constraint, expr, space_def) != 0)
> +		goto error;
> 
> +end:
> +	sql_expr_delete(sql_get(), expr, false);
> 	return ck_constraint;
> error:
> 	ck_constraint_delete(ck_constraint);
> -	return NULL;
> +	ck_constraint = NULL;
> +	goto end;

Trivial refactoring:

@@ -254,13 +250,12 @@ ck_constraint_new(const struct ck_constraint_def *ck_constraint_def,
        if (ck_constraint_test_compile(ck_constraint, expr, space_def) != 0)
                goto error;
 
-end:
        sql_expr_delete(sql_get(), expr, false);
        return ck_constraint;
 error:
        ck_constraint_delete(ck_constraint);
-       ck_constraint = NULL;
-       goto end;
+       sql_expr_delete(sql_get(), expr, false);
+       return NULL;
 }

> diff --git a/src/box/ck_constraint.h b/src/box/ck_constraint.h
> index 02aa525ce..77b1a9bed 100644
> --- a/src/box/ck_constraint.h
> +++ b/src/box/ck_constraint.h
> @@ -32,6 +32,7 @@
>  */
> 
> #include <stdint.h>
> +#include "trigger.h"
> #include "small/rlist.h”

You can remove this header: trigger.h includes it as well.

> 
> #if defined(__cplusplus)
> @@ -40,6 +41,7 @@ extern "C" {
> 
> struct space;
> struct space_def;
> +struct sql_stmt;
> struct Expr;
> 
> /**
> @@ -72,17 +74,29 @@ struct ck_constraint {
> 	 */
> 	struct ck_constraint_def *def;
> 	/**
> -	 * The check constraint expression AST is built for
> -	 * ck_constraint::def::expr_str with sql_expr_compile
> -	 * and resolved with sqlResolveExprNames for
> -	 * space with space[ck_constraint::space_id] definition.
> +	 * Precompiled reusable VDBE program for proceeding ck

proceeding -> processing

ck constraint checks -> check constraints

> +	 * constraint checks and setting bad exitcode and error
> +	 * message when ck condition unsatisfied.
> +	 * Program rely on new_tuple_var parameter to be binded

binded in -> bound to 

> +	 * in the VDBE memory before run.
> 	 */
> -	struct Expr *expr;
> +	struct sql_stmt *stmt;
> 	/**
> 	 * The id of the space this check constraint is
> 	 * built for.
> 	 */
> 	uint32_t space_id;
> +	/**
> +	 * The first ck_constraint::stmt VDBE variable of the
> +	 * range space[ck_constraint::space_id]->def->field_count
> +	 * representing a new tuple to be inserted.
> +	 */
> +	int new_tuple_var;
> +	/**
> +	 * Trigger object executing check constraint on space
> +	 * insert and replace.
> +	 */
> +	struct trigger trigger;
> 	/**
> 	 * Organize check constraint structs into linked list
> 	 * with space::ck_constraint.
> diff --git a/src/box/sql.c b/src/box/sql.c
> index fc469126e..912b24bf0 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -29,6 +29,7 @@
>  * SUCH DAMAGE.
>  */
> #include <assert.h>
> +#include "bind.h"
> #include "field_def.h"
> #include "cfg.h"
> #include "sql.h"
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 2fe74a027..3caad3c24 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -798,51 +798,26 @@ sqlInsert(Parse * pParse,	/* Parser context */
> 	sqlDbFree(db, aRegIdx);
> }
> 
> -/*
> - * Meanings of bits in of pWalker->eCode for checkConstraintUnchanged()
> - */
> -#define CKCNSTRNT_COLUMN   0x01	/* CHECK constraint uses a changing column */
> -
> -/* This is the Walker callback from checkConstraintUnchanged().  Set
> - * bit 0x01 of pWalker->eCode if
> - * pWalker->eCode to 0 if this expression node references any of the
> - * columns that are being modifed by an UPDATE statement.
> - */
> -static int
> -checkConstraintExprNode(Walker * pWalker, Expr * pExpr)
> -{
> -	if (pExpr->op == TK_COLUMN) {
> -		assert(pExpr->iColumn >= 0 || pExpr->iColumn == -1);
> -		if (pExpr->iColumn >= 0) {
> -			if (pWalker->u.aiCol[pExpr->iColumn] >= 0) {
> -				pWalker->eCode |= CKCNSTRNT_COLUMN;
> -			}
> -		}
> -	}
> -	return WRC_Continue;
> -}
> -
> -/*
> - * pExpr is a CHECK constraint on a row that is being UPDATE-ed.  The
> - * only columns that are modified by the UPDATE are those for which
> - * aiChng[i]>=0.
> - *
> - * Return true if CHECK constraint pExpr does not use any of the
> - * changing columns.  In other words, return true if this CHECK constraint
> - * can be skipped when validating the new row in the UPDATE statement.
> - */
> -static int
> -checkConstraintUnchanged(Expr * pExpr, int *aiChng)
> +void
> +vdbe_emit_ck_constraint(struct Parse *parser, struct Expr *expr,
> +			const char *name, int new_tuple_reg)
> {
> -	Walker w;
> -	memset(&w, 0, sizeof(w));
> -	w.eCode = 0;
> -	w.xExprCallback = checkConstraintExprNode;
> -	w.u.aiCol = aiChng;
> -	sqlWalkExpr(&w, pExpr);
> -	testcase(w.eCode == 0);
> -	testcase(w.eCode == CKCNSTRNT_COLUMN);
> -	return !w.eCode;
> +	parser->ckBase = new_tuple_reg;
> +	struct Vdbe *v = sqlGetVdbe(parser);
> +	const char *ck_constraint_name = sqlDbStrDup(parser->db, name);

Where's this pointer released?

> +	VdbeNoopComment((v, "BEGIN: ck constraint %s test", name));
> +	/* Skip check when it is turned off. */

When it can be turned off?

> +	int all_is_ok = sqlVdbeMakeLabel(v);

all_is_ok -> check_is_passed

> +	sqlExprIfTrue(parser, expr, all_is_ok, SQL_JUMPIFNULL);
> +	sqlMayAbort(parser);
> +	const char *fmt = tnt_errcode_desc(ER_CK_CONSTRAINT_FAILED);
> +	const char *error_msg = tt_sprintf(fmt, ck_constraint_name);
> +	sqlVdbeAddOp4(v, OP_Halt, SQL_TARANTOOL_ERROR,
> +		      ON_CONFLICT_ACTION_ABORT, 0,
> +		      sqlDbStrDup(parser->db, error_msg), P4_DYNAMIC);
> +	sqlVdbeChangeP5(v, ER_CK_CONSTRAINT_FAILED);
> +	VdbeNoopComment((v, "END: ck constraint %s test", name));
> +	sqlVdbeResolveLabel(v, all_is_ok);
> }
> 
> 
> diff --git a/test/sql/checks.test.lua b/test/sql/checks.test.lua
> index 9a7e5faf4..0e6c990c9 100644
> --- a/test/sql/checks.test.lua
> +++ b/test/sql/checks.test.lua
> @@ -27,9 +27,11 @@ box.space._ck_constraint:insert({'CK_CONSTRAINT_01', 513, 'X<5'})
> box.space._ck_constraint:count({})
> 
> box.sql.execute("INSERT INTO \"test\" VALUES(5);")
> +box.space.test:insert({5})
> box.space._ck_constraint:replace({'CK_CONSTRAINT_01', 513, 'X<=5'})
> box.sql.execute("INSERT INTO \"test\" VALUES(5);")
> box.sql.execute("INSERT INTO \"test\" VALUES(6);")
> +box.space.test:insert({6})
> -- Can't drop table with check constraints.
> box.space.test:delete({5})
> box.space.test.index.pk:drop()
> @@ -41,8 +43,11 @@ box.space.test:drop()
> box.sql.execute("CREATE TABLE t1(x INTEGER CONSTRAINT ONE CHECK( x<5 ), y REAL CONSTRAINT TWO CHECK( y>x ), z INTEGER PRIMARY KEY);")
> box.space._ck_constraint:count()
> box.sql.execute("INSERT INTO t1 VALUES (7, 1, 1)")
> +box.space.T1:insert({7, 1, 1})
> box.sql.execute("INSERT INTO t1 VALUES (2, 1, 1)")
> +box.space.T1:insert({2, 1, 1})
> box.sql.execute("INSERT INTO t1 VALUES (2, 4, 1)")
> +box.space.T1:update({1}, {{'+', 1, 5}})
> box.sql.execute("DROP TABLE t1”)

Please, add descent set of tests verifying that check constraints
work in any possible scenario.
Make sure that check occurs before replace action.

  reply	other threads:[~2019-04-02 14:14 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-30  8:59 [tarantool-patches] [PATCH v2 0/9] sql: Checks on server side Kirill Shcherbatov
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 1/9] box: fix upgrade script for _fk_constraint space Kirill Shcherbatov
2019-03-11 18:44   ` [tarantool-patches] " n.pettik
2019-03-13 11:36   ` Kirill Yukhin
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 2/9] box: fix _trigger and _ck_constraint access check Kirill Shcherbatov
2019-03-11 19:29   ` [tarantool-patches] " n.pettik
2019-03-22  9:29     ` Vladislav Shpilevoy
2019-03-26 10:59     ` Kirill Shcherbatov
2019-04-01 14:06       ` n.pettik
2019-03-13 11:38   ` Kirill Yukhin
2019-03-13 11:44     ` Kirill Yukhin
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 3/9] box: fix Tarantool upgrade from 2.1.0 to 2.1.1 Kirill Shcherbatov
2019-03-12 11:45   ` [tarantool-patches] " n.pettik
2019-03-20 15:12     ` n.pettik
2019-03-20 15:38       ` Kirill Shcherbatov
2019-03-21 15:23         ` n.pettik
2019-03-21 15:36           ` Vladislav Shpilevoy
2019-03-22  9:28         ` Vladislav Shpilevoy
2019-03-22 10:18           ` Kirill Shcherbatov
2019-03-22 10:21             ` Vladislav Shpilevoy
2019-03-26  9:52   ` Kirill Yukhin
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 4/9] box: fix on_replace_trigger_rollback routine Kirill Shcherbatov
2019-03-11 20:00   ` [tarantool-patches] " n.pettik
2019-03-13 11:39   ` Kirill Yukhin
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 5/9] schema: add new system space for CHECK constraints Kirill Shcherbatov
2019-03-22  9:29   ` [tarantool-patches] " Vladislav Shpilevoy
2019-03-22  9:52     ` n.pettik
2019-03-26 10:59     ` Kirill Shcherbatov
2019-04-01 19:45       ` n.pettik
2019-04-16 13:51         ` Kirill Shcherbatov
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 6/9] sql: disallow use of TYPEOF in Check Kirill Shcherbatov
2019-03-26 10:59   ` [tarantool-patches] " Kirill Shcherbatov
2019-04-01 19:52     ` n.pettik
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 7/9] sql: refactor sqlite3_reset routine Kirill Shcherbatov
2019-03-26 10:59   ` [tarantool-patches] " Kirill Shcherbatov
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 8/9] box: exported sql_bind structure and API Kirill Shcherbatov
2019-03-26 10:59   ` [tarantool-patches] " Kirill Shcherbatov
2019-01-30  8:59 ` [tarantool-patches] [PATCH v2 9/9] sql: run check constraint tests on space alter Kirill Shcherbatov
2019-03-26 10:59   ` [tarantool-patches] " Kirill Shcherbatov
2019-04-02 14:14     ` n.pettik [this message]
2019-04-16 13:51       ` Kirill Shcherbatov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=121C5F34-691B-4FCC-A1C9-C995683D4A6A@tarantool.org \
    --to=korablev@tarantool.org \
    --cc=kshcherbatov@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --subject='[tarantool-patches] Re: [PATCH v2 9/9] sql: run check constraint tests on space alter' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox