[tarantool-patches] Re: [PATCH] sql: allow any space symbols to be a white space
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Tue May 22 21:06:26 MSK 2018
Hello. Thanks for the patch! At first, no one test is passed on my
machine. I see infinite errors like this:
[012] Worker "012_box" cannot start tarantool server; the tasks will be ignored...
[012] The raised exception is '' of type '<class 'lib.tarantool_server.TarantoolStartError'>'.
[012] Worker "012_box" received the following error:
[012] Traceback (most recent call last):
[012] File "/Users/v.shpilevoy/Work/Repositories/tarantool/test-run/lib/worker.py", line 211, in __init__
[012] self.inspector = suite.start_server(self.server)
[012] File "/Users/v.shpilevoy/Work/Repositories/tarantool/test-run/lib/test_suite.py", line 158, in start_server
[012] server.deploy(silent=False)
[012] File "/Users/v.shpilevoy/Work/Repositories/tarantool/test-run/lib/tarantool_server.py", line 510, in deploy
[012] self.start(silent=silent, **kwargs)
[012] File "/Users/v.shpilevoy/Work/Repositories/tarantool/test-run/lib/tarantool_server.py", line 578, in start
[012] self.wait_until_started(wait_load)
[012] File "/Users/v.shpilevoy/Work/Repositories/tarantool/test-run/lib/tarantool_server.py", line 745, in wait_until_started
[012] msg, self.process if not self.gdb and not self.lldb else None)
[012] File "/Users/v.shpilevoy/Work/Repositories/tarantool/test-run/lib/tarantool_server.py", line 211, in seek_wait
[012] raise TarantoolStartError
[012] TarantoolStartError
[012]
On Travis I see the same.
At second, see 17 comments below. Most of them are about style, but a pair
of functional ones too.
On 22/05/2018 18:51, Kirill Yukhin wrote:
> Branch: https://github.com/tarantool/tarantool/tree/kyukhin/gh-2371-utf8-spaces
> Issue: https://github.com/tarantool/tarantool/issues/2371
>
> ANSI SQL allows any of Unicode classes ZI, Zp or Zs to
> act as white space symbol. Allow this in lexical analyzer.
> Refactor lexical analyzer routine to follow Tarantool's
> coding style.
> Also, remove dead encoding: ABCDIC.
1. Maybe EBCDIC?
>
> Closes #2371
> ---
> src/box/sql/alter.c | 13 +-
> src/box/sql/complete.c | 16 --
> src/box/sql/func.c | 5 -
> src/box/sql/global.c | 22 --
> src/box/sql/sqliteInt.h | 45 ++--
> src/box/sql/tokenize.c | 631 +++++++++++++++++++++---------------------------
> src/box/sql/util.c | 5 -
> src/box/sql/vdbetrace.c | 2 +-
> src/box/sql/whereexpr.c | 4 -
> 9 files changed, 291 insertions(+), 452 deletions(-)
>
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index c9c8f9b..f509d4e 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -351,7 +351,7 @@ rename_table(sqlite3 *db, const char *sql_stmt, const char *table_name,
>
> int token;
> Token old_name;
> - unsigned char const *csr = (unsigned const char *)sql_stmt;
> + char const *csr = sql_stmt;
2. const char *
> int len = 0;
> char *new_sql_stmt;
> bool unused;
> diff --git a/src/box/sql/complete.c b/src/box/sql/complete.c
> index 092d4fb..047e09e 100644
> --- a/src/box/sql/complete.c
> +++ b/src/box/sql/complete.c
> @@ -40,19 +40,6 @@
> #include "sqliteInt.h"
> #ifndef SQLITE_OMIT_COMPLETE
>
> -/*
> - * This is defined in tokenize.c. We just have to import the definition.
> - */
> -#ifndef SQLITE_AMALGAMATION
> -#ifdef SQLITE_ASCII
> -#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0)
> -#endif
> -#ifdef SQLITE_EBCDIC
3. I still can grep SQLITE_EBCDIC in tarantool/extra/mkkeywordhash.c. It is not
generated file. Same about SQLITE_ASCII.
> -extern const char sqlite3IsEbcdicIdChar[];
> -#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
> -#endif
> -#endif /* SQLITE_AMALGAMATION */
> -
> /*
> * Token types used by the sqlite3_complete() routine. See the header
> * comments on that procedure for additional information.
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index b3db468..f55c734 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -36,6 +36,8 @@
> #ifndef SQLITEINT_H
> #define SQLITEINT_H
>
> +#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0)
4. Why can not you leave it in sql/complete.c?
> @@ -4164,7 +4136,18 @@ extern int sqlite3PendingByte;
> #endif
> void sqlite3Reindex(Parse *, Token *, Token *);
> void sqlite3AlterRenameTable(Parse *, SrcList *, Token *);
> -int sqlite3GetToken(const unsigned char *, int *, bool *);
> +
> +/**
> + * Return the length (in bytes) of the token that begins at z[0].
> + * Store the token type in *tokenType before returning.
5. No tokenType.
> + *
> + * @param z Input stream.
> + * @param[out] type Detected type of token.
> + * @param[out] is_reserved True if reserved word.
> + */
> +int
> +sql_token(const char *z, int *type, bool *is_reserved);
> +
> void sqlite3NestedParse(Parse *, const char *, ...);
> void sqlite3ExpirePreparedStatements(sqlite3 *);
> int sqlite3CodeSubselect(Parse *, Expr *, int);
> diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
> index c77aa9b..8df58e9 100644
> --- a/src/box/sql/tokenize.c
> +++ b/src/box/sql/tokenize.c
> @@ -77,10 +81,9 @@
> #define CC_DOT 26 /* '.' */
> #define CC_ILLEGAL 27 /* Illegal character */
>
> -static const unsigned char aiClass[] = {
> -#ifdef SQLITE_ASCII
> +static const char sql_ascii_class[] = {
> /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
> -/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
> +/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 7, 7, 7, 27, 27,
6. Why did you change 27 to 7 in xb column?
> /* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
> /* 2x */ 7, 15, 9, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
> /* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
> @@ -96,63 +99,16 @@ static const unsigned char aiClass[] = {
> /* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
> /* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
> /* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
> -#endif
> -#ifdef SQLITE_EBCDIC
> -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
> -/* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27,
> - 27,
> -/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
> -/* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
> -/* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
> -/* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 12, 17, 20, 10,
> -/* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27,
> -/* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 6,
> -/* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8,
> -/* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
> -/* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
> -/* Ax */ 27, 25, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
> -/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27,
> -/* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
> -/* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
> -/* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
> -/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27,
> -#endif
> };
>
> -/*
> +/**
> * The charMap() macro maps alphabetic characters (only) into their
7. Out of 66.
> * lower-case ASCII equivalent. On ASCII machines, this is just
> - * an upper-to-lower case map. On EBCDIC machines we also need
> - * to adjust the encoding. The mapping is only valid for alphabetics
> - * which are the only characters for which this feature is used.
> + * an upper-to-lower case map.
> *
> * Used by keywordhash.h
> */
> -#ifdef SQLITE_ASCII
> #define charMap(X) sqlite3UpperToLower[(unsigned char)X]
> -#endif
> -#ifdef SQLITE_EBCDIC
> -#define charMap(X) ebcdicToAscii[(unsigned char)X]
> -const unsigned char ebcdicToAscii[] = {
> -/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3x */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5x */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, /* 6x */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7x */
> - 0, 97, 98, 99, 100, 101, 102, 103, 104, 105, 0, 0, 0, 0, 0, 0, /* 8x */
> - 0, 106, 107, 108, 109, 110, 111, 112, 113, 114, 0, 0, 0, 0, 0, 0, /* 9x */
> - 0, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 0, 0, 0, 0, 0, /* Ax */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Bx */
> - 0, 97, 98, 99, 100, 101, 102, 103, 104, 105, 0, 0, 0, 0, 0, 0, /* Cx */
> - 0, 106, 107, 108, 109, 110, 111, 112, 113, 114, 0, 0, 0, 0, 0, 0, /* Dx */
> - 0, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 0, 0, 0, 0, 0, /* Ex */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Fx */
> -};
> -#endif
>
> /*
> * The sqlite3KeywordCode function looks up an identifier to determine if
> @@ -167,360 +123,313 @@ const unsigned char ebcdicToAscii[] = {
> */
> #include "keywordhash.h"
>
> -/*
> +/**
> * If X is a character that can be used in an identifier then
> - * IdChar(X) will be true. Otherwise it is false.
> + * sql_id_char(X) will be true. Otherwise it is false.
> *
> - * For ASCII, any character with the high-order bit set is
> + * Any character with the high-order bit set is
> * allowed in an identifier. For 7-bit characters,
> * sqlite3IsIdChar[X] must be 1.
8. No sqlite3IsIdChar.
9. Now I see two identical implementations of this macro:
sql_id_char here and IdChar in sqliteInt.h. And sql_id_char looks
to be unused.
> *
> - * For EBCDIC, the rules are more complex but have the same
> - * end result.
> - *
> - * Ticket #1066. the SQL standard does not allow '$' in the
> + * The SQL standard does not allow '$' in the
> * middle of identifiers. But many SQL implementations do.
> * SQLite will allow '$' in identifiers for compatibility.
> * But the feature is undocumented.
> + *
10. Trailing white space.
> + * @param C Character to classify.
> */
> -#ifdef SQLITE_ASCII
> -#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0)
> -#endif
> -#ifdef SQLITE_EBCDIC
> -const char sqlite3IsEbcdicIdChar[] = {
> -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
> - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 4x */
> - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, /* 5x */
> - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 6x */
> - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, /* 7x */
> - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, /* 8x */
> - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, /* 9x */
> - 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, /* Ax */
> - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Bx */
> - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Cx */
> - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Dx */
> - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* Ex */
> - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* Fx */
> -};
> +#define sql_id_char(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0)
>
> -#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
> -#endif
> +/**
> + * Return true if current symbol is space.
> + *
> + * @param z Input stream.
> + * @retval True if current symbl space.
11. symbol.
> + */
> +static inline bool
> +sql_is_space_char(const char *z)
> +{
> + if (sqlite3Isspace(z[0]))
> + return true;
> + if (sqlite3CtypeMap[*(unsigned char*)z] & 0x40) {
12. Why 0x40?
> + UChar32 c;
> + int unused = 0;
> + U8_NEXT(z, unused, INT32_MAX, c);
> + if (u_isspace(c))
> + return true;
> + }
> + return false;
> +}
>
> -/*
> - * Return the length (in bytes) of the token that begins at z[0].
> - * Store the token type in *tokenType before returning.
> +/**
> + * Calculate length of continuous sequence of
> + * space symbols.
> + *
> + * @param z Input stream.
> + * @retval Number of bytes which constitute sequence of spaces.
> + * Can be 0 if first symbol in stram is not space.
> */
> +static inline int
> +sql_skip_spaces(const char *z)
> +{
> + int idx = 0;
> + while (true) {
> + if (sqlite3Isspace(z[idx])) {
> + idx += 1;
> + } else if ((sqlite3CtypeMap[*(unsigned char *)(z + idx)] & 0x40)
13. Already second place with 0x40. What is it? Maybe you should create a separate
macro like it is done here?
#define sqlite3Isspace(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x01)
#define sqlite3Isalnum(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x06)
#define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02)
#define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04)
#define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08)
...
> + != 0) {
> + UChar32 c;
> + int prev_offset = idx;
> + U8_NEXT(z, idx, INT32_MAX, c);
14. If you do not check for errors and don't know a length,
you can use U8_NEXT_UNSAFE. It is much shorter. Same about U8_NEXT
in sql_is_space_char I think.
> + if (!u_isspace(c)) {
> + idx = prev_offset;
15. In most cases you will have one white space, so maybe it is faster
to make this:
int new_offset = idx;
U8_NEXT(z, new_offset, INT32_MAX, c);
if (! u_isspace(c))
break;
idx = new_offset;
Here you have not to reset the offset in most cases.
> + break;
> + }
> + } else {
> + break;
> + }
> + }
> + return idx;
> +}
> +
> int
> -sqlite3GetToken(const unsigned char *z, int *tokenType, bool *is_reserved)
> +sql_token(const char *z, int *type, bool *is_reserved)
16. Please, consider my light refactoring.
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 8df58e949..4cdd375ee 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -209,8 +209,7 @@ sql_token(const char *z, int *type, bool *is_reserved)
return i;
case CC_MINUS:
if (z[1] == '-') {
- for (i = 2; (c = z[i]) != 0 && c != '\n'; i++) {
- }
+ for (i = 2; (c = z[i]) != 0 && c != '\n'; i++);
*type = TK_SPACE;
return i;
}
@@ -237,10 +236,8 @@ sql_token(const char *z, int *type, bool *is_reserved)
return 1;
}
for (i = 3, c = z[2];
- (c != '*' || z[i] != '/') && (c = z[i]) != 0;
- i++) {
- }
- if (c)
+ (c != '*' || z[i] != '/') && (c = z[i]) != 0; i++);
+ if (c != 0)
i++;
*type = TK_SPACE;
return i;
@@ -323,8 +320,7 @@ sql_token(const char *z, int *type, bool *is_reserved)
}
FALLTHROUGH;
case CC_DOT:
- if (!sqlite3Isdigit(z[1]))
- {
+ if (! sqlite3Isdigit(z[1])) {
*type = TK_DOT;
return 1;
}
@@ -335,38 +331,32 @@ sql_token(const char *z, int *type, bool *is_reserved)
FALLTHROUGH;
case CC_DIGIT:
*type = TK_INTEGER;
- if (z[0] == '0' && (z[1] == 'x' || z[1] == 'X')
- && sqlite3Isxdigit(z[2])) {
- for (i = 3; sqlite3Isxdigit(z[i]); i++) {
- }
+ if (z[0] == '0' && (z[1] == 'x' || z[1] == 'X') &&
+ sqlite3Isxdigit(z[2])) {
+ for (i = 3; sqlite3Isxdigit(z[i]); i++);
return i;
}
- for (i = 0; sqlite3Isdigit(z[i]); i++) {
- }
+ for (i = 0; sqlite3Isdigit(z[i]); i++);
if (z[i] == '.') {
- i++;
- while (sqlite3Isdigit(z[i]))
- i++;
+ while (sqlite3Isdigit(z[++i]));
*type = TK_FLOAT;
}
if ((z[i] == 'e' || z[i] == 'E') &&
(sqlite3Isdigit(z[i + 1])
- || ((z[i + 1] == '+' || z[i + 1] == '-')
- && sqlite3Isdigit(z[i + 2])))) {
- i += 2;
- while (sqlite3Isdigit(z[i]))
- i++;
+ || ((z[i + 1] == '+' || z[i + 1] == '-') &&
+ sqlite3Isdigit(z[i + 2])))) {
+ ++i;
+ while (sqlite3Isdigit(z[++i]));
*type = TK_FLOAT;
}
- while (IdChar(z[i])) {
+ if (IdChar(z[i])) {
*type = TK_ILLEGAL;
- i++;
+ while(IdChar(z[++i]));
}
return i;
case CC_VARNUM:
*type = TK_VARIABLE;
- for (i = 1; sqlite3Isdigit(z[i]); i++) {
- }
+ for (i = 1; sqlite3Isdigit(z[i]); i++);
return i;
case CC_DOLLAR:
case CC_VARALPHA:
@@ -383,9 +373,8 @@ sql_token(const char *z, int *type, bool *is_reserved)
return i;
case CC_KYWD:
for (i = 1; sql_ascii_class[*(unsigned char*)(z+i)] <= CC_KYWD;
- i++) {
- }
- if (!sql_is_space_char((const char *)z + i) && IdChar(z[i])) {
+ i++);
+ if (!sql_is_space_char(z + i) && IdChar(z[i])) {
/* This token started out using characters
* that can appear in keywords, but z[i] is
* a character not allowed within keywords,
@@ -395,18 +384,17 @@ sql_token(const char *z, int *type, bool *is_reserved)
break;
}
*type = TK_ID;
- return keywordCode((char *)z, i, type, is_reserved);
+ return keywordCode(z, i, type, is_reserved);
case CC_X:
if (z[1] == '\'') {
*type = TK_BLOB;
- for (i = 2; sqlite3Isxdigit(z[i]); i++) {
- }
+ for (i = 2; sqlite3Isxdigit(z[i]); i++);
if (z[i] != '\'' || i % 2) {
*type = TK_ILLEGAL;
- while (z[i] && z[i] != '\'')
+ while (z[i] != 0 && z[i] != '\'')
i++;
}
- if (z[i])
+ if (z[i] != 0)
i++;
return i;
}
17. And the last comment. I see, that you use sql_is_space_char to
detect if a char is a white space. But the caller code propagates the
offset to 1 byte always. Regardless of the fact the white space symbol
can occupy more than 1 byte.
> if (!sql_is_space_char(z + i) && IdChar(z[i])) {
> /* This token started out using characters
> * that can appear in keywords, but z[i] is
> * a character not allowed within keywords,
> * so this must be an identifier instead.
> */
> i++;
^^^^^^^^^
\|/
here --------+
> break;
> }
I propose to inline it and propagate 'i' on the value got from
U8_NEXT_UNSAFE as offset.
More information about the Tarantool-patches
mailing list