[tarantool-patches] [PATCH v2 1/4] sql: get rid off sqlite3NestedParse in clean stats

  • From: Kirill Shcherbatov <kshcherbatov@xxxxxxxxxxxxx>
  • To: tarantool-patches@xxxxxxxxxxxxx
  • Date: Tue, 10 Jul 2018 20:08:08 +0300

Now we manually generate AST structures to drop outdated
stats from _sql_stat1 and _sql_stat4 spaces instead of
starting sqlite3NestedParse. This function become totally
useless and could be removed.

Part of #3496.
---
 src/box/sql/analyze.c   | 102 ++++++++++++++++++++----------------------------
 src/box/sql/build.c     |  79 ++++++++++++++++++++++++++-----------
 src/box/sql/sqliteInt.h |  13 ++++++
 3 files changed, 113 insertions(+), 81 deletions(-)

diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 5f73f02..336d146 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -116,71 +116,53 @@
 #include "tarantoolInt.h"
 #include "vdbeInt.h"
 
-/*
- * This routine generates code that opens the sql_statN tables.
- * The _sql_stat1 table is always relevant. _sql_stat4 is only opened when
- * appropriate compile-time options are provided.
- *
- * If the sql_statN tables do not previously exist, it is created.
+/**
+ * This routine generates code that opens the sql_stat1/4 tables.
+ * If the sql_statN tables do not previously exist, they are
+ * created.
  *
- * Argument zWhere may be a pointer to a buffer containing a table name,
- * or it may be a NULL pointer. If it is not NULL, then all entries in
- * the sql_statN tables associated with the named table are deleted.
- * If zWhere==0, then code is generated to delete all stat table entries.
+ * @param parse Parsing context.
+ * @param stat_cursor Open the _sql_stat1 table on this cursor.
+ * @param index_name Delete records of this table if specified.
+ * @param table_name Delete records of this index if specified.
  */
 static void
-openStatTable(Parse * pParse,  /* Parsing context */
-             int iStatCur,     /* Open the _sql_stat1 table on this cursor */
-             const char *zWhere,       /* Delete entries for this table or 
index */
-             const char *zWhereType    /* Either "tbl" or "idx" */
-    )
+vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
+                         const char *index_name, const char *table_name)
 {
-       const char *aTable[] = {
-               "_sql_stat1",
-               "_sql_stat4",
-               NULL};
-       int i;
-       sqlite3 *db = pParse->db;
-       Vdbe *v = sqlite3GetVdbe(pParse);
-       int aRoot[ArraySize(aTable)];
-       u8 aCreateTbl[ArraySize(aTable)];
+       const char *space_names[] = {"_sql_stat1", "_sql_stat4"};
+       const uint32_t space_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
+       struct Vdbe *v = sqlite3GetVdbe(parse);
+       assert(v != NULL);
+       assert(sqlite3VdbeDb(v) == parse->db);
 
-       if (v == 0)
-               return;
-       assert(sqlite3VdbeDb(v) == db);
-
-       /* Create new statistic tables if they do not exist, or clear them
-        * if they do already exist.
+       /*
+        * Create new statistic tables if they do not exist, or
+        * clear them if they do already exist.
         */
-       for (i = 0; aTable[i]; i++) {
-               const char *zTab = aTable[i];
-               Table *pStat;
-               /* The table already exists, because it is a system space */
-               pStat = sqlite3HashFind(&db->pSchema->tblHash, zTab);
-               assert(pStat != NULL);
-               aRoot[i] = pStat->tnum;
-               aCreateTbl[i] = 0;
-               if (zWhere) {
-                       sqlite3NestedParse(pParse,
-                                          "DELETE FROM \"%s\" WHERE \"%s\"=%Q",
-                                          zTab, zWhereType, zWhere);
+       for (uint i = 0; i < lengthof(space_names); ++i) {
+               const char *space_name = space_names[i];
+               /*
+                * The table already exists, because it is a
+                * system space.
+                */
+               assert(sqlite3HashFind(&parse->db->pSchema->tblHash,
+                                      space_name) != NULL);
+               if (table_name != NULL || index_name != NULL) {
+                       vdbe_emit_stat_space_clear(parse, space_name,
+                                                  index_name, table_name);
                } else {
-                       /*
-                        * The sql_stat[134] table already exists.
-                        * Delete all rows.
-                        */
-                       sqlite3VdbeAddOp2(v, OP_Clear,
-                                         SQLITE_PAGENO_TO_SPACEID(aRoot[i]), 
0);
+                       sqlite3VdbeAddOp2(v, OP_Clear, space_ids[i], 0);
                }
        }
 
-       /* Open the sql_stat[134] tables for writing. */
-       for (i = 0; aTable[i]; i++) {
-               struct space *space =
-                       space_by_id(SQLITE_PAGENO_TO_SPACEID(aRoot[i]));
-               vdbe_emit_open_cursor(pParse, iStatCur + i, aRoot[i], space);
-               sqlite3VdbeChangeP5(v, aCreateTbl[i]);
-               VdbeComment((v, aTable[i]));
+       /* Open the sql_stat tables for writing. */
+       for (uint i = 0; i < lengthof(space_names); ++i) {
+               uint32_t id = space_ids[i];
+               int tnum = SQLITE_PAGENO_FROM_SPACEID_AND_INDEXID(id, 0);
+               vdbe_emit_open_cursor(parse, stat_cursor + i, tnum,
+                                     space_by_id(id));
+               VdbeComment((v, space_names[i]));
        }
 }
 
@@ -1117,7 +1099,7 @@ sql_analyze_database(Parse *parser)
        sql_set_multi_write(parser, false);
        int stat_cursor = parser->nTab;
        parser->nTab += 3;
-       openStatTable(parser, stat_cursor, NULL, NULL);
+       vdbe_emit_stat_space_open(parser, stat_cursor, NULL, NULL);
        int reg = parser->nMem + 1;
        int tab_cursor = parser->nTab;
        for (struct HashElem *k = sqliteHashFirst(&schema->tblHash); k != NULL;
@@ -1145,10 +1127,12 @@ analyzeTable(Parse * pParse, Table * pTab, Index * 
pOnlyIdx)
        sql_set_multi_write(pParse, false);
        iStatCur = pParse->nTab;
        pParse->nTab += 3;
-       if (pOnlyIdx) {
-               openStatTable(pParse, iStatCur, pOnlyIdx->zName, "idx");
+       if (pOnlyIdx != NULL) {
+               vdbe_emit_stat_space_open(pParse, iStatCur, pOnlyIdx->zName,
+                                         NULL);
        } else {
-               openStatTable(pParse, iStatCur, pTab->def->name, "tbl");
+               vdbe_emit_stat_space_open(pParse, iStatCur, NULL,
+                                         pTab->def->name);
        }
        analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem + 1,
                        pParse->nTab);
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 0072f84..21791a4 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -2050,6 +2050,60 @@ sql_store_select(struct Parse *parse_context, struct 
Select *select)
 }
 
 /**
+ * Create expression record "@col_name = '@col_value'".
+ *
+ * @param parse The parsing context.
+ * @param col_name Name of column.
+ * @param col_value Name of row.
+ * @retval not NULL on success.
+ * @retval NULL on failure.
+ */
+static struct Expr *
+sql_id_eq_str_expr(struct Parse *parse, const char *col_name,
+                  const char *col_value)
+{
+       struct sqlite3 *db = parse->db;
+
+       struct Expr *col_name_expr = sqlite3Expr(db, TK_ID, col_name);
+       if (col_name_expr == NULL)
+               return NULL;
+       struct Expr *col_value_expr = sqlite3Expr(db, TK_STRING, col_value);
+       if (col_value_expr == NULL) {
+               sql_expr_delete(db, col_name_expr, false);
+               return NULL;
+       }
+       return sqlite3PExpr(parse, TK_EQ, col_name_expr, col_value_expr);
+}
+
+void
+vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name,
+                          const char *idx_name, const char *table_name)
+{
+       assert(idx_name != NULL || table_name != NULL);
+       struct sqlite3 *db = parse->db;
+       assert(!db->mallocFailed);
+       struct SrcList *src_list = sql_alloc_src_list(db);
+       if (src_list != NULL)
+               src_list->a[0].zName = sqlite3DbStrDup(db, stat_table_name);
+       struct Expr *where = NULL;
+       if (idx_name != NULL) {
+               struct Expr *expr = sql_id_eq_str_expr(parse, "idx", idx_name);
+               if (expr != NULL)
+                       where = sqlite3ExprAnd(db, expr, where);
+       }
+       if (table_name != NULL) {
+               struct Expr *expr = sql_id_eq_str_expr(parse, "tbl", 
table_name);
+               if (expr != NULL)
+                       where = sqlite3ExprAnd(db, expr, where);
+       }
+       /**
+        * On memory allocation error sql_table delete_from
+        * releases memory for its own.
+        */
+       sql_table_delete_from(parse, src_list, where);
+}
+
+/**
  * Remove entries from the _sql_stat1 and _sql_stat4
  * system spaces after a DROP INDEX or DROP TABLE command.
  *
@@ -2059,30 +2113,11 @@ sql_store_select(struct Parse *parse_context, struct 
Select *select)
  * @param idx_name   Index to be dropped.
  */
 static void
-sql_clear_stat_spaces(Parse *parse, const char *table_name,
+sql_clear_stat_spaces(struct Parse *parse, const char *table_name,
                      const char *idx_name)
 {
-       if (idx_name != NULL) {
-               sqlite3NestedParse(parse,
-                                  "DELETE FROM \"_sql_stat1\" "
-                                  "WHERE (\"idx\"=%Q AND "
-                                  "\"tbl\"=%Q)",
-                                  idx_name, table_name);
-               sqlite3NestedParse(parse,
-                                  "DELETE FROM \"_sql_stat4\" "
-                                  "WHERE (\"idx\"=%Q AND "
-                                  "\"tbl\"=%Q)",
-                                  idx_name, table_name);
-       } else {
-               sqlite3NestedParse(parse,
-                                  "DELETE FROM \"_sql_stat1\" "
-                                  "WHERE \"tbl\"=%Q",
-                                  table_name);
-               sqlite3NestedParse(parse,
-                                  "DELETE FROM \"_sql_stat4\" "
-                                  "WHERE \"tbl\"=%Q",
-                                  table_name);
-       }
+       vdbe_emit_stat_space_clear(parse, "_sql_stat4", idx_name, table_name);
+       vdbe_emit_stat_space_clear(parse, "_sql_stat1", idx_name, table_name);
 }
 
 /**
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 8b75ae8..d76d173 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -4847,4 +4847,17 @@ vdbe_emit_halt_with_presence_test(struct Parse *parser, 
int space_id,
                                  const char *error_src, bool no_error,
                                  int cond_opcode);
 
+/**
+ * Generate VDBE code to delete records from system _sql_stat1 or
+ * _sql_stat4 table.
+ *
+ * @param parse The parsing context.
+ * @param stat_table_name System stat table name.
+ * @param idx_name Index name.
+ * @param table_name Table name.
+ */
+void
+vdbe_emit_stat_space_clear(struct Parse *parse, const char *stat_table_name,
+                          const char *idx_name, const char *table_name);
+
 #endif                         /* SQLITEINT_H */
-- 
2.7.4


Other related posts: