Fix issue: Fix bugs on the index advisor: String overlength related bugs

This commit is contained in:
flyly
2021-08-03 11:49:04 +08:00
parent 53f100a843
commit 30af811c4f

View File

@ -53,12 +53,11 @@
#define MAX_SAMPLE_ROWS 10000 /* sampling range for executing a query */
#define CARDINALITY_THRESHOLD 30 /* the threshold of index selection */
#define MAX_QUERY_LEN 512
#define MAX_FIELD_LEN 128
#define MAX_QUERY_LEN 2048
typedef struct {
char table[NAMEDATALEN];
char column[NAMEDATALEN];
StringInfoData column;
} SuggestedIndex;
typedef struct {
@ -96,7 +95,6 @@ THR_LOCAL TableCell *g_driver_table = NULL;
static SuggestedIndex *suggest_index(const char *, _out_ int *);
static StmtResult *execute_stmt(const char *query_string, bool need_result = false);
static inline void analyze_tables(List *list);
static List *get_table_indexes(Oid oid);
static List *get_index_attname(Oid oid);
static char *search_table_attname(Oid attrelid, int2 attnum);
@ -110,7 +108,7 @@ static void find_select_stmt(Node *);
static void extract_stmt_from_clause(List *);
static void extract_stmt_where_clause(Node *);
static void parse_where_clause(Node *);
static void field_value_trans(_out_ char *, A_Const *);
static void field_value_trans(_out_ StringInfoData, A_Const *);
static void parse_field_expr(List *, List *, List *);
static inline uint4 tuple_to_uint(List *);
static uint4 get_table_count(const char *);
@ -192,7 +190,7 @@ Datum gs_index_advise(PG_FUNCTION_ARGS)
/* Locking is probably not really necessary */
values[0] = CStringGetTextDatum(entry->table);
values[1] = CStringGetTextDatum(entry->column);
values[1] = CStringGetTextDatum(entry->column.data);
tuple = heap_form_tuple(func_ctx->tuple_desc, values, nulls);
SRF_RETURN_NEXT(func_ctx, HeapTupleGetDatum(tuple));
@ -295,26 +293,20 @@ SuggestedIndex *suggest_index(const char *query_string, _out_ int *len)
rc = strcpy_s((array + i)->table, NAMEDATALEN, cur_table->table_name);
securec_check(rc, "\0", "\0");
initStringInfo(&(array + i)->column);
if (!index_list) {
rc = strcpy_s((array + i)->column, NAMEDATALEN, "");
securec_check(rc, "\0", "\0");
appendStringInfo(&(array + i)->column, "");
} else {
ListCell *cur_index = NULL;
int j = 0;
foreach (cur_index, index_list) {
if (strlen((char *)lfirst(cur_index)) + strlen((array + i)->column) + 3 > NAMEDATALEN) {
continue;
}
if (j > 0) {
rc = strcat_s((array + i)->column, NAMEDATALEN, ",(");
appendStringInfo(&(array + i)->column, ",(");
} else {
rc = strcpy_s((array + i)->column, NAMEDATALEN, "(");
appendStringInfo(&(array + i)->column, "(");
}
securec_check(rc, "\0", "\0");
rc = strcat_s((array + i)->column, NAMEDATALEN, (char *)lfirst(cur_index));
securec_check(rc, "\0", "\0");
rc = strcat_s((array + i)->column, NAMEDATALEN, ")");
securec_check(rc, "\0", "\0");
appendStringInfo(&(array + i)->column, (char *)lfirst(cur_index));
appendStringInfo(&(array + i)->column, ")");
j++;
}
}
@ -342,23 +334,6 @@ void free_global_resource()
g_driver_table = NULL;
}
/* Update table statistics obtained from query tree by executing the 'analyze table' statement. */
inline void analyze_tables(List *list)
{
ListCell *item = NULL;
foreach (item, list) {
RangeTblEntry *entry = (RangeTblEntry *)lfirst(item);
if (entry != NULL && entry->relname != NULL) {
errno_t rc = EOK;
char stmt[MAX_QUERY_LEN] = {0x00};
rc = sprintf_s(stmt, MAX_QUERY_LEN, "analyze %s;", entry->relname);
securec_check_ss_c(rc, "\0", "\0");
(void)execute_stmt(stmt);
}
}
}
/* Search the oid of all indexes created on the table through the oid of the table,
* and return the index oid list.
* */
@ -652,7 +627,8 @@ void extract_stmt_where_clause(Node *item_where)
*/
void generate_final_index(TableCell *table, Oid table_oid)
{
char *suggested_index = (char *)palloc0(NAMEDATALEN);
int suggested_index_len = NAMEDATALEN * table->index->length;
char *suggested_index = (char *)palloc0(suggested_index_len);
ListCell *index = NULL;
errno_t rc = EOK;
int i = 0;
@ -660,15 +636,15 @@ void generate_final_index(TableCell *table, Oid table_oid)
// concatenate the candidate indexes into a string
foreach (index, table->index) {
char *index_name = ((IndexCell *)lfirst(index))->index_name;
if (strlen(index_name) + strlen(suggested_index) + 1 > NAMEDATALEN) {
if (strlen(index_name) + strlen(suggested_index) + 1 > suggested_index_len) {
break;
}
if (i == 0) {
rc = strcpy_s(suggested_index, NAMEDATALEN, index_name);
rc = strcpy_s(suggested_index, suggested_index_len, index_name);
} else {
rc = strcat_s(suggested_index, NAMEDATALEN, ",");
rc = strcat_s(suggested_index, suggested_index_len, ",");
securec_check(rc, "\0", "\0");
rc = strcat_s(suggested_index, NAMEDATALEN, index_name);
rc = strcat_s(suggested_index, suggested_index_len, index_name);
}
securec_check(rc, "\0", "\0");
i++;
@ -686,22 +662,22 @@ void generate_final_index(TableCell *table, Oid table_oid)
}
// check the existed indexes
char *existed_index = (char *)palloc0(NAMEDATALEN);
List *indexes = get_table_indexes(table_oid);
index = NULL;
foreach (index, indexes) {
List *attnames = get_index_attname(lfirst_oid(index));
char *existed_index = (char *)palloc0(NAMEDATALEN * attnames->length);
ListCell *item = NULL;
i = 0;
foreach (item, attnames) {
if (i == 0) {
rc = strcpy_s(existed_index, NAMEDATALEN, (char *)lfirst(item));
rc = strcpy_s(existed_index, NAMEDATALEN * attnames->length, (char *)lfirst(item));
} else {
rc = strcat_s(existed_index, NAMEDATALEN, ",");
rc = strcat_s(existed_index, NAMEDATALEN * attnames->length, ",");
securec_check(rc, "\0", "\0");
rc = strcat_s(existed_index, NAMEDATALEN, (char *)lfirst(item));
rc = strcat_s(existed_index, NAMEDATALEN * attnames->length, (char *)lfirst(item));
}
securec_check(rc, "\0", "\0");
i++;
@ -712,9 +688,9 @@ void generate_final_index(TableCell *table, Oid table_oid)
pfree(existed_index);
return;
}
pfree(existed_index);
}
table->index_print = lappend(table->index_print, suggested_index);
pfree(existed_index);
return;
}
@ -945,21 +921,16 @@ void parse_join_expr(JoinExpr *join_tree)
}
// convert field value to string
void field_value_trans(_out_ char *target, A_Const *field_value)
void field_value_trans(_out_ StringInfoData target, A_Const *field_value)
{
Value value = field_value->val;
if (value.type == T_Integer) {
pg_itoa(value.val.ival, target);
pg_itoa(value.val.ival, target.data);
} else if (value.type == T_String) {
if (strlen(value.val.str) > MAX_FIELD_LEN) {
return;
}
errno_t rc = sprintf_s(target, MAX_QUERY_LEN, "'%s'", value.val.str);
securec_check_ss_c(rc, "\0", "\0");
appendStringInfo(&target, "'%s'", value.val.str);
}
}
/*
* parse_field_expr
* Parse the field expression and add index.
@ -977,72 +948,74 @@ void parse_field_expr(List *field, List *op, List *lfield_values)
}
char *op_type = strVal(linitial(op));
char *field_expr = (char *)palloc0(MAX_QUERY_LEN);
char *field_value = (char *)palloc0(MAX_QUERY_LEN);
StringInfoData field_expr;
initStringInfo(&field_expr);
StringInfoData field_value;
initStringInfo(&field_value);
ListCell *item = NULL;
int i = 0;
// get field values
foreach (item, lfield_values) {
char *str = (char *)palloc0(MAX_QUERY_LEN);
StringInfoData str;
initStringInfo(&str);
if (!IsA(lfirst(item), A_Const)) {
continue;
}
field_value_trans(str, (A_Const *)lfirst(item));
if (strlen(str) == 0) {
if (strlen(str.data) == 0) {
continue;
}
if (i == 0) {
rc = strcpy_s(field_value, MAX_QUERY_LEN, str);
appendStringInfo(&field_value, str.data);
} else {
rc = strcat_s(field_value, MAX_QUERY_LEN, ",");
securec_check(rc, "\0", "\0");
rc = strcat_s(field_value, MAX_QUERY_LEN, str);
appendStringInfo(&field_value, ",%s", str.data);
}
securec_check(rc, "\0", "\0");
i++;
pfree(str);
pfree_ext(str.data);
}
if (i == 0) {
pfree(field_value);
pfree(field_expr);
pfree(field_value.data);
pfree(field_expr.data);
return;
}
// get field expression, e.g., 'id = 100'
if (strcasecmp(op_type, "~~") == 0) {
// ...like...
if (field_value != NULL && field_value[1] == '%') {
pfree(field_value);
pfree(field_expr);
if (field_value.data != NULL && field_value.data[1] == '%') {
pfree_ext(field_value.data);
pfree_ext(field_expr.data);
return;
} else {
rc = sprintf_s(field_expr, MAX_QUERY_LEN, "%s like %s", index_name, field_value);
appendStringInfo(&field_expr, "%s like %s", index_name, field_value.data);
}
} else if (lfield_values->length > 1) {
// ...(not)in...
if (strcasecmp(op_type, "=") == 0) {
rc = sprintf_s(field_expr, MAX_QUERY_LEN, "%s in (%s)", index_name, field_value);
appendStringInfo(&field_expr, "%s in (%s)", index_name, field_value.data);
} else {
rc = sprintf_s(field_expr, MAX_QUERY_LEN, "%s not in (%s)", index_name, field_value);
appendStringInfo(&field_expr, "%s not in (%s)", index_name, field_value.data);
}
} else {
// ...>=<...
rc = sprintf_s(field_expr, MAX_QUERY_LEN, "%s %s %s", index_name, op_type, field_value);
appendStringInfo(&field_expr, "%s %s %s", index_name, op_type, field_value.data);
}
securec_check_ss_c(rc, "\0", "\0");
pfree(field_value);
pfree_ext(field_value.data);
uint4 cardinality = calculate_field_cardinality(table_name, field_expr);
uint4 cardinality = calculate_field_cardinality(table_name, field_expr.data);
if (cardinality > CARDINALITY_THRESHOLD) {
IndexCell *index = (IndexCell *)palloc0(sizeof(*index));
index->index_name = index_name;
index->cardinality = cardinality;
index->op = op_type;
index->field_expr = field_expr;
index->field_expr = field_expr.data;
add_index_from_field(table_name, index);
}
pfree_ext(field_expr.data);
}
inline uint4 tuple_to_uint(List *tuples)
@ -1056,7 +1029,7 @@ uint4 get_table_count(const char *table_name)
char query_string[MAX_QUERY_LEN] = {0x00};
errno_t rc =
sprintf_s(query_string, MAX_QUERY_LEN, "select reltuples from pg_class where relname='%s';", table_name);
securec_check_ss_c(rc, "\0", "\0");
securec_check_ss(rc, "\0", "\0");
StmtResult *result = execute_stmt(query_string, true);
table_count = tuple_to_uint(result->tuples);
@ -1072,12 +1045,13 @@ uint4 calculate_field_cardinality(const char *table_name, const char *field_expr
uint4 cardinality;
uint4 table_count = get_table_count(table_name);
uint4 sample_rows = (table_count / sample_factor) > MAX_SAMPLE_ROWS ? MAX_SAMPLE_ROWS : (table_count / sample_factor);
char query_string[MAX_QUERY_LEN] = {0x00};
errno_t rc = sprintf_s(query_string, MAX_QUERY_LEN, "select count(*) from ( select * from %s limit %d) where %s",
StringInfoData query_string;
initStringInfo(&query_string);
appendStringInfo(&query_string, "select count(*) from ( select * from %s limit %d) where %s",
table_name, sample_rows, field_expr);
securec_check_ss_c(rc, "\0", "\0");
StmtResult *result = execute_stmt(query_string, true);
StmtResult *result = execute_stmt(query_string.data, true);
pfree_ext(query_string.data);
uint4 row = tuple_to_uint(result->tuples);
(*result->pub.rDestroy)((DestReceiver *)result);
@ -1143,7 +1117,7 @@ char *find_table_name(List *fields)
errno_t rc = sprintf_s(query_string, MAX_QUERY_LEN,
"select count(*) from information_schema.columns where table_name = '%s' and column_name = '%s'",
table_name, column_name);
securec_check_ss_c(rc, "\0", "\0");
securec_check_ss(rc, "\0", "\0");
StmtResult *result = execute_stmt(query_string, true);
if (result) {
@ -1361,25 +1335,28 @@ void determine_driver_table()
uint4 get_join_table_result_set(const char *table_name, const char *condition)
{
char query_string[MAX_QUERY_LEN] = {0x00};
errno_t rc = EOK;
StringInfoData query_string;
initStringInfo(&query_string);
if (condition == NULL) {
rc = sprintf_s(query_string, MAX_QUERY_LEN, "select * from %s;", table_name);
appendStringInfo(&query_string, "select * from %s;", table_name);
} else {
rc = sprintf_s(query_string, MAX_QUERY_LEN, "select * from %s where %s;", table_name, condition);
appendStringInfo(&query_string, "select * from %s where %s;", table_name, condition);
}
securec_check_ss_c(rc, "\0", "\0");
/* get query execution plan */
List *parsetree_list = pg_parse_query(query_string, NULL);
List *parsetree_list = pg_parse_query(query_string.data, NULL);
ListCell *parsetree_item = list_head(parsetree_list);
Node *parsetree = (Node *)lfirst(parsetree_item);
List *querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0);
List *querytree_list = pg_analyze_and_rewrite(parsetree, query_string.data, NULL, 0);
List *plantree_list = pg_plan_queries(querytree_list, 0, NULL);
PlannedStmt *plan_stmt = (PlannedStmt *)lfirst(list_head(plantree_list));
Scan *scan = (Scan *)(plan_stmt->planTree);
return (uint4)scan->plan.plan_rows;
uint4 rows = (uint4)scan->plan.plan_rows;
pfree_ext(query_string.data);
list_free_ext(parsetree_list);
list_free_ext(plantree_list);
list_free_ext(querytree_list);
return rows;
}
void add_index_from_join(TableCell *table, char *index_name)