From 3fb196910de05ceb72551ade600e934636a43247 Mon Sep 17 00:00:00 2001 From: Salil Chandra Date: Tue, 14 Jan 2025 17:03:36 -0500 Subject: [PATCH] Use op codes to form query plan instead of using query path Signed-off-by: Salil Chandra --- db/db_fingerprint.c | 2 +- db/db_query_plan.c | 43 +++++++++++++++++++++-------- db/sql.h | 2 +- db/sqlexplain.c | 33 +++++++++++----------- db/sqlexplain.h | 3 ++ tests/fingerprints.test/t07.req.out | 8 +++--- 6 files changed, 57 insertions(+), 34 deletions(-) diff --git a/db/db_fingerprint.c b/db/db_fingerprint.c index 8ec3e433d1..3679b2b088 100644 --- a/db/db_fingerprint.c +++ b/db/db_fingerprint.c @@ -208,7 +208,7 @@ void add_fingerprint(struct sqlclntstate *clnt, sqlite3_stmt *stmt, struct strin char *params = NULL; int calc_query_plan = gbl_query_plans && !is_lua; if (calc_query_plan) { - query_plan_ref = form_query_plan(clnt->query_stats); + query_plan_ref = form_query_plan(stmt); calc_fingerprint(query_plan_ref ? string_ref_cstr(query_plan_ref) : NULL, &temp, plan_fingerprint); if (gbl_sample_queries && param_count(clnt) > 0) { // only get params string if we need it diff --git a/db/db_query_plan.c b/db/db_query_plan.c index 2a969c87f9..a7397aa3c7 100644 --- a/db/db_query_plan.c +++ b/db/db_query_plan.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include int gbl_query_plan_max_plans = 20; extern double gbl_query_plan_percentage; @@ -29,26 +31,43 @@ extern hash_t *gbl_fingerprint_hash; extern pthread_mutex_t gbl_fingerprint_hash_mu; // return NULL if no plan -struct string_ref *form_query_plan(const struct client_query_stats *query_stats) +struct string_ref *form_query_plan(sqlite3_stmt *stmt) { - struct strbuf *query_plan_buf; - const struct client_query_path_component *c; struct string_ref *query_plan_ref; + Op *op; + struct cursor_info c; + Vdbe *v = (Vdbe *)stmt; + char *operation; - if (query_stats->n_components == 0) { + if (!v) return NULL; - } - query_plan_buf = strbuf_new(); - for (int i = 0; i < query_stats->n_components; i++) { - if (i > 0) { + struct strbuf *query_plan_buf = strbuf_new(); + for (int pc = 0; pc < v->nOp; pc++) { + op = &v->aOp[pc]; + if (op->opcode == OP_OpenRead) + operation = "read"; + else if (op->opcode == OP_ReopenIdx) + operation = "(re)read"; + else if (op->opcode == OP_OpenRead_Record) + operation = "read"; + else if (op->opcode == OP_OpenWrite) + operation = "write"; + else + continue; + + if (strbuf_len(query_plan_buf) > 0) strbuf_append(query_plan_buf, ", "); - } - c = &query_stats->path_stats[i]; - strbuf_appendf(query_plan_buf, "table %s index %d", c->table, c->ix); + + strbuf_appendf(query_plan_buf, "open %s cursor on ", operation); + describe_cursor(v, pc, &c); + if (c.tbl >= 0) + print_cursor_description(query_plan_buf, &c, 0); + else + strbuf_append(query_plan_buf, "table \"???\""); } - query_plan_ref = create_string_ref((char *)strbuf_buf(query_plan_buf)); + query_plan_ref = strbuf_len(query_plan_buf) > 0 ? create_string_ref((char *)strbuf_buf(query_plan_buf)) : NULL; strbuf_free(query_plan_buf); return query_plan_ref; } diff --git a/db/sql.h b/db/sql.h index 40c9ff4834..2c069d854f 100644 --- a/db/sql.h +++ b/db/sql.h @@ -1533,7 +1533,7 @@ struct query_plan_item { }; int free_query_plan_hash(hash_t *query_plan_hash); int clear_query_plans(); -struct string_ref *form_query_plan(const struct client_query_stats *query_stats); +struct string_ref *form_query_plan(sqlite3_stmt *stmt); void add_query_plan(int64_t cost, int64_t nrows, struct fingerprint_track *t, struct string_ref *zSql_ref, struct string_ref *query_plan_ref, unsigned char *plan_fingerprint, char *params); diff --git a/db/sqlexplain.c b/db/sqlexplain.c index 1fe64910d9..5c4d35ffac 100644 --- a/db/sqlexplain.c +++ b/db/sqlexplain.c @@ -165,7 +165,7 @@ static void print_field(Vdbe *v, struct cursor_info *cinfo, int num, char *buf) } } -static int print_cursor_description(strbuf *out, struct cursor_info *cinfo) +int print_cursor_description(strbuf *out, struct cursor_info *cinfo, int append_space) { struct schema *sc; char scname[100]; @@ -222,7 +222,8 @@ static int print_cursor_description(strbuf *out, struct cursor_info *cinfo) } strbuf_appendf(out, "table \"%s\"", db->tablename); } - strbuf_appendf(out, " "); + if (append_space) + strbuf_appendf(out, " "); return is_index; } @@ -366,7 +367,7 @@ static void affinity_to_text(char *aff, strbuf *out) extern int sqlite3WhereTrace; -static void describe_cursor(Vdbe *v, int pc, struct cursor_info *cur) +void describe_cursor(Vdbe *v, int pc, struct cursor_info *cur) { Op *op = &v->aOp[pc]; bzero(cur, sizeof(struct cursor_info)); @@ -752,7 +753,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, print_field(v, &cur[op->p1], op->p2, buf); /* field name into buf */ strbuf_appendf(out, "R%d = %s from cursor [%d] on ", op->p3, buf, op->p1); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); } else { Op *colOp = &v->aOp[pc]; op = &v->aOp[pc_]; @@ -803,7 +804,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, case OP_Count: strbuf_appendf(out, "R%d = select count(*) from cursor [%d] on ", op->p2, op->p1); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); break; case OP_Savepoint: strbuf_appendf( @@ -833,7 +834,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, describe_cursor(v, pc, &cur[op->p1]); strbuf_appendf(out, "Open read cursor [%d] if not already open on ", op->p1); - int is_index = print_cursor_description(out, &cur[op->p1]); + int is_index = print_cursor_description(out, &cur[op->p1], 1); if (is_index && op->opcode == OP_OpenRead) { if (op->p5 == 0xff) { strbuf_append(out, "(not a covering index)"); @@ -848,7 +849,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, describe_cursor(v, pc, &cur[op->p1]); strbuf_appendf(out, "Open %s cursor [%d] on ", (op->opcode != OP_OpenWrite ? "read" : "write"), op->p1); - is_index = print_cursor_description(out, &cur[op->p1]); + is_index = print_cursor_description(out, &cur[op->p1], 1); if (is_index && op->opcode == OP_OpenRead) { if (op->p5 == 0xff) { strbuf_append(out, "(not a covering index)"); @@ -961,13 +962,13 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, break; case OP_Insert: strbuf_appendf(out, "Write record in R%d into ", op->p2); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); strbuf_appendf(out, " using cursor [%d]", op->p1); break; case OP_Delete: strbuf_appendf(out, "Delete current record from cursor [%d] on", op->p1); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); break; case OP_ResetCount: strbuf_append( @@ -976,7 +977,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, case OP_RowData: strbuf_appendf(out, "R%d = key or data from cursor [%d] on ", op->p2, op->p1); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); break; case OP_Rowid: strbuf_appendf(out, "R%d = genid of row pointed by cursor [%d]", op->p2, @@ -987,7 +988,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, break; case OP_Last: strbuf_appendf(out, "Move cursor [%d] on ", op->p1); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); strbuf_append(out, "to last entry. "); if (op->p2) strbuf_appendf(out, "If no entries exist, go to %d", op->p2); @@ -1018,7 +1019,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, break; case OP_IdxInsert: strbuf_appendf(out, "Write key in R%d into ", op->p2); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); strbuf_appendf(out, "using cursor [%d]", op->p1); break; case OP_IdxDelete: @@ -1029,7 +1030,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, strbuf_appendf(out, "R%d", op->p2); } strbuf_appendf(out, " from "); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); strbuf_appendf(out, "using cursor [%d]", op->p1); break; case OP_IdxRowid: @@ -1052,11 +1053,11 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, break; case OP_Destroy: strbuf_append(out, "Destroy "); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); break; case OP_Clear: strbuf_append(out, "Delete all rows from "); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); break; case OP_RowSetAdd: strbuf_appendf(out, "Insert R%d into boolean index in R%d", op->p2, @@ -1116,7 +1117,7 @@ void get_one_explain_line(sqlite3 *hndl, strbuf *out, Vdbe *v, int indent, break; case OP_CursorHint: strbuf_appendf(out, "Cursor [%d] table ", op->p1); - print_cursor_description(out, &cur[op->p1]); + print_cursor_description(out, &cur[op->p1], 1); char *descr = sqlite3ExprDescribe(hndl->pVdbe, op->p4.pExpr); strbuf_appendf(out, " hint \"%s\"", (descr) ? descr : "(expression not parseable, see 592)"); diff --git a/db/sqlexplain.h b/db/sqlexplain.h index c8a1034e85..d59e6f324a 100644 --- a/db/sqlexplain.h +++ b/db/sqlexplain.h @@ -39,4 +39,7 @@ typedef struct { void explain_data_prepare(IndentInfo *p, Vdbe *v); void explain_data_delete(IndentInfo *p); +int print_cursor_description(strbuf *out, struct cursor_info *cinfo, int append_space); +void describe_cursor(Vdbe *v, int pc, struct cursor_info *cur); + #endif /* _SQLEXPLAIN_H_ */ diff --git a/tests/fingerprints.test/t07.req.out b/tests/fingerprints.test/t07.req.out index e4f77cfa51..61215394a1 100644 --- a/tests/fingerprints.test/t07.req.out +++ b/tests/fingerprints.test/t07.req.out @@ -29,12 +29,12 @@ (a=29, b=30, c=31, d=32) (a=33, b=34, c=35, d=36) (a=37, b=38, c=39, d=40) -(fingerprint='f57a9e3c17f13606af3630d8ebb27f3f', plan_fingerprint='6dc6fddfacf59d6af9b6ca29a1d1448d', normalized_sql='SELECT*FROM t2 WHERE a