Skip to content

Commit

Permalink
Use op codes to form query plan instead of using query path
Browse files Browse the repository at this point in the history
Signed-off-by: Salil Chandra <[email protected]>
  • Loading branch information
chands10 committed Jan 24, 2025
1 parent faa452e commit 3fb1969
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 34 deletions.
2 changes: 1 addition & 1 deletion db/db_fingerprint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 31 additions & 12 deletions db/db_query_plan.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <math.h>
#include <ctrace.h>
#include <inttypes.h>
#include <sqlexplain.h>
#include <vdbeInt.h>

int gbl_query_plan_max_plans = 20;
extern double gbl_query_plan_percentage;
Expand All @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion db/sql.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
33 changes: 17 additions & 16 deletions db/sqlexplain.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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_];
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)");
Expand All @@ -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)");
Expand Down Expand Up @@ -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(
Expand All @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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,
Expand Down Expand Up @@ -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)");
Expand Down
3 changes: 3 additions & 0 deletions db/sqlexplain.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
8 changes: 4 additions & 4 deletions tests/fingerprints.test/t07.req.out
Original file line number Diff line number Diff line change
Expand Up @@ -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<?;', plan='table t2 index 0', total_cost_per_row=4.000000, num_executions=2, avg_cost_per_row=2.000000)
(fingerprint='f57a9e3c17f13606af3630d8ebb27f3f', plan_fingerprint='70e60685f4bc838817d0490e36773f33', normalized_sql='SELECT*FROM t2 WHERE a<?;', plan='table t2 index -1', total_cost_per_row=3.500000, num_executions=1, avg_cost_per_row=3.500000)
(fingerprint='f57a9e3c17f13606af3630d8ebb27f3f', plan_fingerprint='7342e5366727e8781044a411fd58d5b3', normalized_sql='SELECT*FROM t2 WHERE a<?;', plan='open read cursor on index "A" of table "t2"', total_cost_per_row=4.000000, num_executions=2, avg_cost_per_row=2.000000)
(fingerprint='f57a9e3c17f13606af3630d8ebb27f3f', plan_fingerprint='1312f58a03a7afe964921b51794c5900', normalized_sql='SELECT*FROM t2 WHERE a<?;', plan='open read cursor on table "t2"', total_cost_per_row=3.500000, num_executions=1, avg_cost_per_row=3.500000)
(fingerprint='bb1b8ba88d815c2bac18af4618bae9dc', plan_fingerprint='00000000000000000000000000000000', normalized_sql='SELECT*FROM comdb2_query_plans WHERE fingerprint=?;', plan=NULL, total_cost_per_row=0.000000, num_executions=1, avg_cost_per_row=0.000000)
("test"='test', @test=1)
(fingerprint='f57a9e3c17f13606af3630d8ebb27f3f', plan_fingerprint='6dc6fddfacf59d6af9b6ca29a1d1448d', query='select * from t2 where a < 40', query_plan='table t2 index 0', params=NULL)
(fingerprint='f57a9e3c17f13606af3630d8ebb27f3f', plan_fingerprint='70e60685f4bc838817d0490e36773f33', query='select * from t2 where a < 40; // use worse query plan', query_plan='table t2 index -1', params=NULL)
(fingerprint='f57a9e3c17f13606af3630d8ebb27f3f', plan_fingerprint='1312f58a03a7afe964921b51794c5900', query='select * from t2 where a < 40; // use worse query plan', query_plan='open read cursor on table "t2"', params=NULL)
(fingerprint='f57a9e3c17f13606af3630d8ebb27f3f', plan_fingerprint='7342e5366727e8781044a411fd58d5b3', query='select * from t2 where a < 40', query_plan='open read cursor on index "A" of table "t2"', params=NULL)
(fingerprint='bb1b8ba88d815c2bac18af4618bae9dc', plan_fingerprint='00000000000000000000000000000000', query='select * from comdb2_query_plans where fingerprint='f57a9e3c17f13606af3630d8ebb27f3f'', query_plan=NULL, params=NULL)
(fingerprint='ff590219c73ee93717a6aef16ee0dbe9', plan_fingerprint='00000000000000000000000000000000', query='select "test", @test', query_plan=NULL, params='[{"name":"test","type":"largeint","value":1}]')
("test"='test', 2=2)
Expand Down

0 comments on commit 3fb1969

Please sign in to comment.