[tarantool-patches] Re: [PATCH 6/8] sql: replace affinity with field type in struct Expr

n.pettik korablev at tarantool.org
Mon Jan 28 19:39:57 MSK 2019


>>>> -int
>>>> -sqlite3IndexAffinityOk(Expr * pExpr, char idx_affinity)
>>>> +enum field_type
>>>> +sql_index_type_is_ok(struct Expr *expr, enum field_type idx_type)
>>> 
>>> 4. Strictly speaking, it is not idx_type. It is a type of one
>>> column. I think, it should be renamed to just type, or field_type,
>>> as well as in struct WhereScan.
>> Yea, it’s fair. Renamed:
>> -/*
>> - * pExpr is a comparison operator.  Return the type affinity that should
>> - * be applied to both operands prior to doing the comparison.
>> +/**
>> + * pExpr is a comparison operator. Return the type affinity
>> + * that should be applied to both operands prior to doing
>> + * the comparison.
>>   */
>>  static enum field_type
>> -comparisonAffinity(Expr * pExpr)
>> +expr_cmp_mutual_type(struct Expr *pExpr)
>>  {
>>         assert(pExpr->op == TK_EQ || pExpr->op == TK_IN || pExpr->op == TK_LT ||
>>                pExpr->op == TK_GT || pExpr->op == TK_GE || pExpr->op == TK_LE ||
> 
> Are you sure? Looks like it is not renamed. Also, the comment
> was about sql_index_type_is_ok and its parameter idx_type, but you
> changed comparisonAffinity function.

Sorry, I renamed function, but forgot about param. Fixed:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 1de980f00..1d4ea6ef0 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -281,21 +281,21 @@ expr_cmp_mutual_type(struct Expr *pExpr)
 
 /**
  * @param expr is a comparison expression, eg. '=', '<', IN(...) etc.
- * @param idx_type is the type of an indexed column.
- * @retval Return true if the index with @idx_type may be used to
+ * @param field_type is the type of an indexed column.
+ * @retval Return true if the index with @field_type may be used to
  * implement the comparison in expr.
  */
 enum field_type
-sql_expr_cmp_type_is_compatible(struct Expr *expr, enum field_type idx_type)
+sql_expr_cmp_type_is_compatible(struct Expr *expr, enum field_type field_type)
 {
        enum field_type type = expr_cmp_mutual_type(expr);
        switch (type) {
        case FIELD_TYPE_SCALAR:
                return 1;
        case FIELD_TYPE_STRING:
-               return idx_type == FIELD_TYPE_STRING;
+               return field_type == FIELD_TYPE_STRING;
        default:
-               return sql_type_is_numeric(idx_type);
+               return sql_type_is_numeric(field_type);
        }
 }

>>>>  @@ -2826,16 +2795,15 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
>>>>  				 * that columns affinity when building index keys. If <expr> is not
>>>>  				 * a column, use numeric affinity.
>>>>  				 */
>>>> -				char affinity;	/* Affinity of the LHS of the IN */
>>>>  				int i;
>>>>  				ExprList *pList = pExpr->x.pList;
>>>>  				struct ExprList_item *pItem;
>>>>  				int r1, r2, r3;
>>>>  -				affinity = sqlite3ExprAffinity(pLeft);
>>>> -				if (!affinity) {
>>>> -					affinity = AFFINITY_BLOB;
>>>> -				}
>>>> +				enum field_type lhs_type =
>>>> +					sql_expr_type(pLeft);
>>>> +				if (lhs_type == FIELD_TYPE_ANY)
>>>> +					lhs_type = FIELD_TYPE_SCALAR;
>>> 
>>> 7. Why did not you move this conversion to sql_expr_type? I mean
>>> ANY -> SCALAR.
>> Unfortunately, right now I can’t replace it with ANY. It is still used
>> when IN operator comes with one operand (see EP_Generic flag).
>> I have already sent patch which removes it:
>> https://github.com/tarantool/tarantool/tree/np/gh-3934-IN-operator-fix
> 
> I've already acked the patch. Please, hurry up Kirill Y. to push it,
> rebase this branch, and replace ANY with SCALAR.

I had to add some other fixes to make this happen:

Changes to sql: replace affinity with field type for func:

diff --git a/src/box/sql/date.c b/src/box/sql/date.c
index e452aad73..f32a5841a 100644
--- a/src/box/sql/date.c
+++ b/src/box/sql/date.c
@@ -1307,14 +1307,14 @@ sqlite3RegisterDateTimeFunctions(void)
        static FuncDef aDateTimeFuncs[] = {
 #ifndef SQLITE_OMIT_DATETIME_FUNCS
                DFUNCTION(julianday, -1, 0, 0, juliandayFunc, FIELD_TYPE_NUMBER),
-               DFUNCTION(date, -1, 0, 0, dateFunc, FIELD_TYPE_NUMBER),
-               DFUNCTION(time, -1, 0, 0, timeFunc, FIELD_TYPE_NUMBER),
-               DFUNCTION(datetime, -1, 0, 0, datetimeFunc, FIELD_TYPE_NUMBER),
-               DFUNCTION(strftime, -1, 0, 0, strftimeFunc, FIELD_TYPE_NUMBER),
-               DFUNCTION(current_time, 0, 0, 0, ctimeFunc, FIELD_TYPE_NUMBER),
+               DFUNCTION(date, -1, 0, 0, dateFunc, FIELD_TYPE_STRING),
+               DFUNCTION(time, -1, 0, 0, timeFunc, FIELD_TYPE_STRING),
+               DFUNCTION(datetime, -1, 0, 0, datetimeFunc, FIELD_TYPE_STRING),
+               DFUNCTION(strftime, -1, 0, 0, strftimeFunc, FIELD_TYPE_STRING),
+               DFUNCTION(current_time, 0, 0, 0, ctimeFunc, FIELD_TYPE_STRING),
                DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc,
-                         FIELD_TYPE_NUMBER),
-               DFUNCTION(current_date, 0, 0, 0, cdateFunc, FIELD_TYPE_NUMBER),
+                         FIELD_TYPE_STRING),
+               DFUNCTION(current_date, 0, 0, 0, cdateFunc, FIELD_TYPE_STRING),
 #else
                STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc),
                STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc),
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 347fee563..58e35e0d9 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -3995,7 +3995,7 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
                                break;
                        }
 
-                       if (pDef->ret_type != AFFINITY_UNDEFINED) {
+                       if (pDef->ret_type != FIELD_TYPE_SCALAR) {
                                pExpr->affinity =
                                        sql_field_type_to_affinity(pDef->ret_type);
                        } else {
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 08228fbbe..147f93ae4 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -1733,14 +1733,14 @@ sqlite3RegisterBuiltinFunctions(void)
                FUNCTION(rtrim, 2, 2, 0, trimFunc, FIELD_TYPE_STRING),
                FUNCTION(trim, 1, 3, 0, trimFunc, FIELD_TYPE_STRING),
                FUNCTION(trim, 2, 3, 0, trimFunc, FIELD_TYPE_STRING),
-               FUNCTION(min, -1, 0, 1, minmaxFunc, 0),
-               FUNCTION(min, 0, 0, 1, 0, 0),
+               FUNCTION(min, -1, 0, 1, minmaxFunc, FIELD_TYPE_SCALAR),
+               FUNCTION(min, 0, 0, 1, 0, FIELD_TYPE_SCALAR),
                AGGREGATE2(min, 1, 0, 1, minmaxStep, minMaxFinalize,
-                          SQLITE_FUNC_MINMAX, 0),
-               FUNCTION(max, -1, 1, 1, minmaxFunc, 0),
-               FUNCTION(max, 0, 1, 1, 0, 0),
+                          SQLITE_FUNC_MINMAX, FIELD_TYPE_SCALAR),
+               FUNCTION(max, -1, 1, 1, minmaxFunc, FIELD_TYPE_SCALAR),
+               FUNCTION(max, 0, 1, 1, 0, FIELD_TYPE_SCALAR),
                AGGREGATE2(max, 1, 1, 1, minmaxStep, minMaxFinalize,
-                          SQLITE_FUNC_MINMAX, 0),
+                          SQLITE_FUNC_MINMAX, FIELD_TYPE_SCALAR),
                FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF,
                          FIELD_TYPE_STRING),
                FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH,
@@ -1769,9 +1769,9 @@ sqlite3RegisterBuiltinFunctions(void)
                FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc, FIELD_TYPE_SCALAR),
                FUNCTION(substr, 2, 0, 0, substrFunc, FIELD_TYPE_STRING),
                FUNCTION(substr, 3, 0, 0, substrFunc, FIELD_TYPE_STRING),
-               AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize, 0),
-               AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize, 0),
-               AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize, 0),
+               AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize, FIELD_TYPE_NUMBER),
+               AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize, FIELD_TYPE_NUMBER),
+               AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize, FIELD_TYPE_NUMBER),
                AGGREGATE2(count, 0, 0, 0, countStep, countFinalize,
                           SQLITE_FUNC_COUNT, FIELD_TYPE_INTEGER),
                AGGREGATE(count, 1, 0, 0, countStep, countFinalize,
@@ -1788,9 +1788,10 @@ sqlite3RegisterBuiltinFunctions(void)
 #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
                FUNCTION(unknown, -1, 0, 0, unknownFunc, 0),
 #endif
-               FUNCTION(coalesce, 1, 0, 0, 0, 0),
-               FUNCTION(coalesce, 0, 0, 0, 0, 0),
-               FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE, 0),
+               FUNCTION(coalesce, 1, 0, 0, 0, FIELD_TYPE_SCALAR),
+               FUNCTION(coalesce, 0, 0, 0, 0, FIELD_TYPE_SCALAR),
+               FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE,
+                         FIELD_TYPE_SCALAR),
        };
        sqlite3AnalyzeFunctions();
        sqlite3RegisterDateTimeFunctions();
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 4c6baaac6..cbda48f78 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -637,6 +637,8 @@ resolveExprStep(Walker * pWalker, Expr * pExpr)
                                }
                        } else {
                                is_agg = pDef->xFinalize != 0;
+                               pExpr->affinity =
+                                       sql_field_type_to_affinity(pDef->ret_type);
                                if (pDef->funcFlags & SQLITE_FUNC_UNLIKELY) {
                                        ExprSetProperty(pExpr,
                                                        EP_Unlikely | EP_Skip);
diff --git a/test-run b/test-run
index 02207efd2..4c3f11812 160000
--- a/test-run
+++ b/test-run
@@ -1 +1 @@
-Subproject commit 02207efd2ca44067b76c79bf012f142016d929ae
+Subproject commit 4c3f11812c0c17f08780f31b6a748eef1126b2e2
diff --git a/test/sql-tap/analyze9.test.lua b/test/sql-tap/analyze9.test.lua
index df62a1624..c43437712 100755
--- a/test/sql-tap/analyze9.test.lua
+++ b/test/sql-tap/analyze9.test.lua
@@ -58,6 +58,9 @@ msgpack_decode_sample = function(txt)
         end
         i = i+1
     end
+    if type(decoded_str) == "number" then
+        return tostring(decoded_str)
+    end
     return decoded_str
 end
 
@@ -332,7 +335,7 @@ test:do_execsql_test(
 test:do_execsql_test(
     4.7,
     [[
-        SELECT count(*) FROM "_sql_stat4" WHERE msgpack_decode_sample("sample") IN (34, 68, 102, 136, 170, 204, 238, 272);
+        SELECT count(*) FROM "_sql_stat4" WHERE lrange(msgpack_decode_sample("sample"), 1, 1) IN ('34', '68', '102', '136', '170', '204', '238', '272');
     ]], {
         -- <4.7>
         8
@@ -367,8 +370,8 @@ test:do_execsql_test(
         SELECT msgpack_decode_sample("sample") FROM "_sql_stat4";
     ]], {
         -- <4.9>
-        "x", 1110, 2230, 2750, 3350, 4090, 4470, 4980, 5240, 5280, 5290, 5590, 5920, 
-        5930, 6220, 6710, 7000, 7710, 7830, 7970, 8890, 8950, 9240, 9250, 9680
+        "x", "1110", "2230", "2750", "3350", "4090", "4470", "4980", "5240", "5280", "5290", "5590", "5920",
+        "5930", "6220", "6710", "7000", "7710", "7830", "7970", "8890", "8950", "9240", "9250", "9680"
         -- </4.9>
     })

Additional changes to  sql: replace affinity with field type in struct Expr:

Firstly, I placed several asserts like assert(type != FIELD_TYPE_ANY);
and noted that not for all expressions its true. So I patched sql_expr_type
function:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index fdf00273a..e1a0dfa73 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -74,10 +74,29 @@ sql_expr_type(struct Expr *pExpr)
        case TK_MINUS:
        case TK_STAR:
        case TK_SLASH:
+       case TK_REM:
+       case TK_BITAND:
+       case TK_BITOR:
+       case TK_LSHIFT:
+       case TK_RSHIFT:
                assert(pExpr->pRight != NULL && pExpr->pLeft != NULL);
                enum field_type lhs_type = sql_expr_type(pExpr->pLeft);
                enum field_type rhs_type = sql_expr_type(pExpr->pRight);
                return sql_type_result(rhs_type, lhs_type);
+       case TK_CONCAT:
+                       return FIELD_TYPE_STRING;
+       case TK_CASE:
+               assert(pExpr->x.pList->nExpr >= 2);
+               /*
+                * CASE expression comes at least with one
+                * WHEN and one THEN clauses. So, first
+                * expression always represents WHEN
+                * argument, and the second one - THEN.
+                *
+                * TODO: We must ensure that all THEN clauses
+                * have arguments of the same type.
+                */
+               return sql_expr_type(pExpr->x.pList->a[1].pExpr);
        case TK_LT:
        case TK_GT:
        case TK_EQ:
@@ -92,16 +111,14 @@ sql_expr_type(struct Expr *pExpr)
                 * return INTEGER.
                 */
                return FIELD_TYPE_INTEGER;
-       }
-       /*
-        * In case of unary plus we shouldn't discard
-        * type of operand (since plus always features
-        * NUMERIC type).
-        */
-       if (op == TK_UPLUS) {
+       case TK_UMINUS:
+       case TK_UPLUS:
+       case TK_NO:
+       case TK_BITNOT:
                assert(pExpr->pRight == NULL);
-               return pExpr->pLeft->type;
+               return sql_expr_type(pExpr->pLeft);
        }
+
        return pExpr->type;
 }

Then, I make default type of expression be SCALAR
except for binding params:

diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index ef691cb5c..d6fc41e3b 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -829,6 +829,18 @@ idlist(A) ::= nm(Y).
       case TK_FLOAT:
         p->type = FIELD_TYPE_NUMBER;
         break;
+      case TK_VARIABLE:
+        /*
+         * For variables we set BOOLEAN type since
+         * unassigned bindings will be replaced
+         * with NULL automatically, i.e. without
+         * explicit call of sql_bind_value().
+         */
+        p->type = FIELD_TYPE_BOOLEAN;
+        break;
+      default:
+        p->type = FIELD_TYPE_SCALAR;
+        break;
       }
       p->op = (u8)op;
       p->flags = EP_Leaf;

diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 8a8ccc68c..2cc0a4a78 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1725,6 +1726,8 @@ generateColumnNames(Parse * pParse,       /* Parser context */
                p = pEList->a[i].pExpr;
                if (NEVER(p == 0))
                        continue;
+               if (p->op == TK_VARIABLE)
+                       var_pos[var_count++] = i;
                switch (p->type) {
                case FIELD_TYPE_INTEGER:
                        sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, "INTEGER",
@@ -1742,21 +1745,12 @@ generateColumnNames(Parse * pParse,     /* Parser context */
                        sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, "BLOB",
                                              SQLITE_TRANSIENT);
                        break;
-               default: ;
-                       char *type;
-                       /*
-                        * For variables we set BOOLEAN type since
-                        * unassigned bindings will be replaced
-                        * with NULL automatically, i.e. without
-                        * explicit call of sql_bind_value().
-                        */
-                       if (p->op == TK_VARIABLE) {
-                               var_pos[var_count++] = i;
-                               type = "BOOLEAN";
-                       } else {
-                               type = "UNKNOWN";
-                       }
-                       sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, type,
+               case FIELD_TYPE_BOOLEAN:
+                       sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, "BOOLEAN",
+                                             SQLITE_TRANSIENT);
+                       break;
+               default:
+                       sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, "UNKNOWN",
                                              SQLITE_TRANSIENT);
                }
                if (pEList->a[i].zName) {

Finally, I simplified derived type calculations and removed
all ANY -> SCALAR transformations:

diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 8a8ccc68c..2cc0a4a78 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1973,8 +1967,6 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,            /* Parsing contexts */
        for (uint32_t i = 0; i < pTab->def->field_count; i++) {
                p = a[i].pExpr;
                enum field_type type = sql_expr_type(p);
-               if (type == FIELD_TYPE_ANY)
-                       type = FIELD_TYPE_SCALAR;
                pTab->def->fields[i].affinity = sql_field_type_to_affinity(type);
                pTab->def->fields[i].type = type;
                bool is_found;
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 930b53b2b..44305f7dc 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1169,7 +1169,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,  /* Complete information about t
                int fieldno = idx_pk->key_def->parts[0].fieldno;
                enum field_type fd_type = is_format_set ?
                                          space->def->fields[fieldno].type :
-                                         FIELD_TYPE_ANY;
+                                         FIELD_TYPE_SCALAR;
 
                uint32_t pk_part_count = idx_pk->key_def->part_count;
                if (pk_part_count == 1 && fd_type == FIELD_TYPE_INTEGER) {
diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
index 6ffd5ae84..1fb5fa593 100644
--- a/src/box/sql/whereexpr.c
+++ b/src/box/sql/whereexpr.c
@@ -758,7 +758,7 @@ exprAnalyzeOrTerm(SrcList * pSrc,   /* the FROM clause */
                                        enum field_type lhs =
                                                sql_expr_type(pOrTerm->pExpr->
                                                        pLeft);
-                                       if (rhs != FIELD_TYPE_ANY &&
+                                       if (rhs != FIELD_TYPE_SCALAR &&
                                            rhs != lhs) {
                                                okToChngToIN = 0;
                                        } else {

diff --git a/src/box/sql.c b/src/box/sql.c
index 530ec2384..cb8f6a60a 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -387,8 +387,8 @@ sql_ephemeral_space_create(uint32_t field_count, struct sql_key_info *key_info)
                part->sort_order = SORT_ORDER_ASC;
                if (def != NULL && i < def->part_count) {
                        assert(def->parts[i].type < field_type_MAX);
-                       part->type = def->parts[i].type != FIELD_TYPE_ANY ?
-                                    def->parts[i].type : FIELD_TYPE_SCALAR;
+                       assert(def->parts[i].type != FIELD_TYPE_ANY);
+                       part->type = def->parts[i].type;
                        part->coll_id = def->parts[i].coll_id;
                } else {
                        part->coll_id = COLL_NONE;

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index fdf00273a..e1a0dfa73 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -231,22 +248,9 @@ sql_expr_coll(Parse *parse, Expr *p, bool *is_explicit_coll, uint32_t *coll_id)
 enum field_type
 sql_type_result(enum field_type lhs, enum field_type rhs)
 {
-       if (lhs != FIELD_TYPE_ANY && rhs != FIELD_TYPE_ANY) {
-               if (sql_type_is_numeric(lhs) || sql_type_is_numeric(rhs))
-                       return FIELD_TYPE_NUMBER;
-               else
-                       return FIELD_TYPE_SCALAR;
-       } else if (lhs == FIELD_TYPE_ANY && rhs == FIELD_TYPE_ANY) {
-               /*
-                * Neither side of the comparison is a column.
-                * Compare the results directly.
-                */
-               return FIELD_TYPE_SCALAR;
-       } else {
-               if (lhs == FIELD_TYPE_ANY)
-                       return rhs;
-               return lhs;
-       }
+       if (sql_type_is_numeric(lhs) || sql_type_is_numeric(rhs))
+               return FIELD_TYPE_NUMBER;
+       return FIELD_TYPE_SCALAR;
 }

@@ -2784,8 +2788,6 @@ sqlite3CodeSubselect(Parse * pParse,      /* Parsing context */
 
                                enum field_type lhs_type =
                                        sql_expr_type(pLeft);
-                               if (lhs_type == FIELD_TYPE_ANY)
-                                       lhs_type = FIELD_TYPE_SCALAR;
                                bool unused;
                                sql_expr_coll(pParse, pExpr->pLeft,
                                              &unused, &key_info->parts[0].coll_id);

Test below are changed due to correct calculation of derived
type for unary operators: you may notice that before it was
simple expr->type. However, it didn’t give us original type
of expr: for column, for instance we should fetch it from space_def.

diff --git a/test/sql-tap/where2.test.lua b/test/sql-tap/where2.test.lua
index 9089c97f9..56bec153f 100755
--- a/test/sql-tap/where2.test.lua
+++ b/test/sql-tap/where2.test.lua
@@ -653,14 +653,13 @@ test:do_test(
         "where2-6.9",
         function()
             return queryplan([[
-    -- The + operator removes affinity from the rhs.  No conversions
-    -- occur and the comparison is false.  The result is an empty set.
+    -- The + operator doesn't affect RHS.
     --
     SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a=+b;
   ]])
         end, {
             -- <where2-6.9>
-            "nosort", "T2249B", "*", "T2249A", "*"
+            123, "0123", "nosort", "T2249B", "*", "T2249A", "*"
             -- </where2-6.9>
         })
 
@@ -673,7 +672,7 @@ test:do_test(
   ]])
         end, {
             -- <where2-6.9.2>
-            "nosort", "T2249B", "*", "T2249A", "*"
+            123, "0123","nosort", "T2249B", "*", "T2249A", "*"
             -- </where2-6.9.2>
         })
 
@@ -681,9 +680,6 @@ test:do_test(
         "where2-6.10",
         function()
             return queryplan([[
-    -- Use + on both sides of the comparison to disable indices
-    -- completely.  Make sure we get the same result.
-    --
     SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE +a=+b;
   ]])
         end, {
@@ -753,45 +749,36 @@ test:do_test(
     test:do_test(
         "where2-6.12",
         function()
-            -- In this case, the +b disables the affinity conflict and allows
-            -- the OR optimization to be used again.  The result is now an empty
-            -- set, the same as in where2-6.9.
             return queryplan([[
       SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a=+b OR a='hello';
     ]])
         end, {
             -- <where2-6.12>
-            "nosort", "T2249B", "*", "T2249A", "*"
+            123, "0123", "nosort", "T2249B", "*", "T2249A", "*"
             -- </where2-6.12>
         })
 
     test:do_test(
         "where2-6.12.2",
         function()
-            -- In this case, the +b disables the affinity conflict and allows
-            -- the OR optimization to be used again.  The result is now an empty
-            -- set, the same as in where2-6.9.
             return queryplan([[
       SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a='hello' OR +b=a;
     ]])
         end, {
             -- <where2-6.12.2>
-            "nosort", "T2249B", "*", "T2249A", "*"
+            123, "0123", "nosort", "T2249B", "*", "T2249A", "*"
             -- </where2-6.12.2>
         })
 
     test:do_test(
         "where2-6.12.3",
         function()
-            -- In this case, the +b disables the affinity conflict and allows
-            -- the OR optimization to be used again.  The result is now an empty
-            -- set, the same as in where2-6.9.
             return queryplan([[
       SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE +b=a OR a='hello';
     ]])
         end, {
             -- <where2-6.12.3>
-            "nosort", "T2249B", "*", "T2249A", "*"
+            123, "0123", "nosort", "T2249B", "*", "T2249A", "*"
             -- </where2-6.12.3>
         })

> 
>> After it hits 2.1, I will replace SCALAR with ANY.
> 
> Vice versa? Not SCALAR with ANY, but ANY with SCALAR, I think.

Yep.

>>> ANY differs from SCALAR in only one thing - it is
>>> able to store MP_MAP and MP_ARRAY. So I am slightly bent upon
>>> frequency of ANY usage in SQL, wherein MAP/ARRAY does not exist.
>> But still we can SELECT data from spaces created from Lua.
>> For instance, _fk_constraint features type ARRAY in its format,
>> so we can’t ignore this type even now (IMHO).
> 
> Despite our ability to select complex types, in SQL they are all
> mere binary data with *sub*type msgpack, not array/map type. So
> in SQL we never truly work with anything but SCALAR.

Wait, at the stage of query compiling we don’t operate on
any subtypes. We simply fetch space_def->fields->type,
which may turn out to be FIELD_TYPE_MAP/ARRAY or
whatever.

>> -/*
>> - * Return TRUE if the given expression is a constant which would be
>> - * unchanged by OP_ApplyType with the type given in the second
>> - * argument.
>> - *
>> - * This routine is used to determine if the OP_ApplyType operation
>> - * can be omitted.  When in doubt return FALSE.  A false negative
>> - * is harmless.  A false positive, however, can result in the wrong
>> - * answer.
>> - */
>> int
>> -sqlite3ExprNeedsNoAffinityChange(const Expr * p, char aff)
>> +sql_expr_needs_no_type_change(const Expr *p, enum field_type type)
> 
> 1. const Expr -> const struct Expr

Fixed:

diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 1de980f00..ddce8c13c 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -2117,7 +2117,7 @@ sqlite3ExprCanBeNull(const Expr * p)
 }
 
 int
-sql_expr_needs_no_type_change(const Expr *p, enum field_type type)
+sql_expr_needs_no_type_change(const struct Expr *p, enum field_type type)
 {
        u8 op;
        if (type == FIELD_TYPE_SCALAR)
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index f44235cb6..fac650f62 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -3721,7 +3721,7 @@ int sqlite3ExprCanBeNull(const Expr *);
  * answer.
  */
 int
-sql_expr_needs_no_type_change(const Expr *epr, enum field_type type);
+sql_expr_needs_no_type_change(const struct Expr *expr, enum field_type type);

>> +/**
>> + * Return TRUE if the given expression is a constant which would
>> + * be unchanged by OP_ApplyType with the type given in the second
>> + * argument.
>> + *
>> + * This routine is used to determine if the OP_ApplyType operation
>> + * can be omitted.  When in doubt return FALSE.  A false negative
>> + * is harmless. A false positive, however, can result in the wrong
>> + * answer.
>> + */
>> +int
>> +sql_expr_needs_no_type_change(const Expr *epr, enum field_type type);
> 
> 2. epr -> p, and the same as 1.

Fixed, see above.

>> diff --git a/src/box/sql/vdbemem.c b/src/box/sql/vdbemem.c
>> index cd71641b0..d12a2a833 100644
>> --- a/src/box/sql/vdbemem.c
>> +++ b/src/box/sql/vdbemem.c
>> @@ -1316,8 +1315,8 @@ valueFromExpr(sqlite3 * db,	/* The database connection */
>> 			sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_DYNAMIC);
>> 		}
>> 		if ((op == TK_INTEGER || op == TK_FLOAT)
>> -		    && affinity == AFFINITY_BLOB) {
>> -			sqlite3ValueApplyAffinity(pVal, AFFINITY_REAL);
>> +		    && affinity == FIELD_TYPE_SCALAR) {
>> +			sqlite3ValueApplyAffinity(pVal, FIELD_TYPE_INTEGER);
> 
> 3. Why before your patch it was REAL and now it is INTEGER?

Typo. Fixed in previous patch (while I was renaming affinity vars to type).

>> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
>> index efbc91cf8..33b860f36 100644
>> --- a/src/box/sql/wherecode.c
>> +++ b/src/box/sql/wherecode.c
>> @@ -423,10 +423,11 @@ updateRangeAffinityStr(Expr * pRight,	/* RHS of comparison */
>> {
>> 	int i;
>> 	for (i = 0; i < n; i++) {
>> +		enum field_type type = sql_affinity_to_field_type(zAff[i]);
>> 		Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
>> -		enum affinity_type aff = sqlite3ExprAffinity(p);
>> -		if (sql_affinity_result(aff, zAff[i]) == AFFINITY_BLOB
>> -		    || sqlite3ExprNeedsNoAffinityChange(p, zAff[i])) {
>> +		enum field_type expr_type = sql_expr_type(p);
>> +		if (sql_type_result(expr_type, type) == FIELD_TYPE_SCALAR ||
>> +			sql_expr_needs_no_type_change(p, type)) {
>> 			zAff[i] = AFFINITY_BLOB;
>> 		}
>> 	}
> 
> 4. Something is wrong with formatting. sql_expr_needs_no_type_change should be
> aligned under sql_type_result as a part of the condition.

Fixed:

diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index 33b860f36..bc57efe18 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -427,7 +427,7 @@ updateRangeAffinityStr(Expr * pRight,       /* RHS of comparison */
                Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
                enum field_type expr_type = sql_expr_type(p);
                if (sql_type_result(expr_type, type) == FIELD_TYPE_SCALAR ||
-                       sql_expr_needs_no_type_change(p, type)) {
+                   sql_expr_needs_no_type_change(p, type)) {
                        zAff[i] = AFFINITY_BLOB;
                }
        }

>> @@ -1166,20 +1167,12 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
>> 		}
>> 		struct index_def *idx_pk = space->index[0]->def;
>> 		int fieldno = idx_pk->key_def->parts[0].fieldno;
>> -		char affinity = is_format_set ?
>> -				space->def->fields[fieldno].affinity :
>> -				AFFINITY_BLOB;
>> -		if (affinity == AFFINITY_UNDEFINED) {
>> -			if (idx_pk->key_def->part_count == 1 &&
>> -			    space->def->fields[fieldno].type ==
>> -			    FIELD_TYPE_INTEGER)
>> -				affinity = AFFINITY_INTEGER;
>> -			else
>> -				affinity = AFFINITY_BLOB;
>> -		}
>> +		char fd_type = is_format_set ?
>> +				space->def->fields[fieldno].type :
>> +				FIELD_TYPE_ANY;
>> 
> 
> 5. Why char? And the alignment is slightly wrong. Non-first lines
> should be aligned under is_format_set.

Fixed:

@@ -1167,9 +1167,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,  /* Complete information about t
                }
                struct index_def *idx_pk = space->index[0]->def;
                int fieldno = idx_pk->key_def->parts[0].fieldno;
-               char fd_type = is_format_set ?
-                               space->def->fields[fieldno].type :
-                               FIELD_TYPE_ANY;
+               enum field_type fd_type = is_format_set ?
+                                         space->def->fields[fieldno].type :
+                                         FIELD_TYPE_SCALAR;





More information about the Tarantool-patches mailing list