-
Notifications
You must be signed in to change notification settings - Fork 405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add query performance statistics #7869
Changes from 1 commit
4b8d7b4
6f36dca
5eab017
d363d78
75004d5
21c11aa
3e459e2
0a79cd4
a98b33e
e3b512f
2929831
284cbec
158cc6b
d76eb07
4c5ec5f
f1153ed
ed36827
8e74a90
44154bb
ac3b25e
87c2c96
cd5d408
eb453cf
575517e
1bc09fe
f26c9c5
ccaf5ac
00f9cd5
c7e861b
9e1b40b
ac41168
c5bec55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,7 +76,7 @@ | |
|
||
PG_MODULE_MAGIC; | ||
|
||
#define EDB_STMT_MAGIC_PREFIX "-- {\"query\"" | ||
#define EDB_STMT_MAGIC_PREFIX "-- {" | ||
|
||
/* Location of permanent stats file (valid when database is shut down) */ | ||
#define PGSS_DUMP_FILE PGSTAT_STAT_PERMANENT_DIRECTORY "/edb_stat_statements.stat" | ||
|
@@ -371,6 +371,8 @@ PG_FUNCTION_INFO_V1(edb_stat_statements_reset); | |
PG_FUNCTION_INFO_V1(edb_stat_statements); | ||
PG_FUNCTION_INFO_V1(edb_stat_statements_info); | ||
|
||
const char * | ||
edbss_extract_info_line(const char *s, int* len); | ||
EdbStmtInfo * | ||
edbss_extract_stmt_info(const char* query_str, int query_len); | ||
static inline void | ||
|
@@ -559,46 +561,39 @@ _PG_init(void) | |
ProcessUtility_hook = pgss_ProcessUtility; | ||
} | ||
|
||
const char * | ||
edbss_extract_info_line(const char *s, int *len) { | ||
int prefix_len = strlen(EDB_STMT_MAGIC_PREFIX); | ||
if (*len > prefix_len && strncmp(s, EDB_STMT_MAGIC_PREFIX, prefix_len) == 0) { | ||
const char *rv = s + 3; // skip "-- " | ||
int remaining_len = *len - prefix_len; | ||
int rv_len = 0; | ||
while (rv_len < remaining_len && rv[rv_len] != '\n') | ||
rv_len++; | ||
if (rv_len > 0) { | ||
*len = rv_len; | ||
return rv; | ||
} | ||
} | ||
return NULL; | ||
} | ||
|
||
/* | ||
* Extract EdgeDB query info from the JSON in the leading comment. | ||
* If success, returns a palloc-ed EdbStmtInfo which must be freed | ||
* after usage with edbss_free_stmt_info(). | ||
*/ | ||
EdbStmtInfo * | ||
edbss_extract_stmt_info(const char* query_str, int query_len) { | ||
int prefix_len = strlen(EDB_STMT_MAGIC_PREFIX); | ||
const char *info_str = query_str; | ||
int info_len = 0; | ||
|
||
if (query_len <= prefix_len) | ||
return NULL; | ||
|
||
if (strncmp(info_str, EDB_STMT_MAGIC_PREFIX, prefix_len) == 0) { | ||
const char *c; | ||
info_str += 3; // skip "-- " | ||
c = info_str; | ||
while (*c != '\n' && info_len + 3 < query_len) { | ||
c++; | ||
info_len++; | ||
} | ||
} | ||
int info_len = query_len; | ||
const char *info_str = edbss_extract_info_line(query_str, &info_len); | ||
|
||
if (info_len > 0) { | ||
if (info_str) { | ||
EdbStmtInfo *info = (EdbStmtInfo *) palloc0(sizeof(EdbStmtInfo)); | ||
EdbStmtInfoSemState state = { | ||
.info = info, | ||
.state = EDB_STMT_INFO_PARSE_NOOP, | ||
}; | ||
JsonLexContext *lex = makeJsonLexContextCstringLen( | ||
#if PG_VERSION_NUM >= 170000 | ||
NULL, | ||
info_str, | ||
#else | ||
(char *) info_str, // not actually mutating | ||
#endif | ||
info_len, | ||
PG_UTF8, | ||
true); | ||
JsonSemAction sem = { | ||
.semstate = (void *) &state, | ||
.object_start = edbss_json_struct_start, | ||
|
@@ -608,14 +603,31 @@ edbss_extract_stmt_info(const char* query_str, int query_len) { | |
.object_field_start = edbss_json_ofield_start, | ||
.scalar = edbss_json_scalar, | ||
}; | ||
JsonParseErrorType parse_rv = pg_parse_json(lex, &sem); | ||
freeJsonLexContext(lex); | ||
|
||
if (parse_rv == JSON_SUCCESS) | ||
if ((state.found & EDB_STMT_INFO_PARSE_REQUIRED) == EDB_STMT_INFO_PARSE_REQUIRED) | ||
if (info->query_id != UINT64CONST(0)) | ||
return info; | ||
|
||
while (info_str) { | ||
JsonLexContext *lex = makeJsonLexContextCstringLen( | ||
#if PG_VERSION_NUM >= 170000 | ||
NULL, | ||
info_str, | ||
#else | ||
(char *) info_str, // not actually mutating | ||
#endif | ||
info_len, | ||
PG_UTF8, | ||
true); | ||
JsonParseErrorType parse_rv = pg_parse_json(lex, &sem); | ||
freeJsonLexContext(lex); | ||
|
||
if (parse_rv == JSON_SUCCESS) | ||
if ((state.found & EDB_STMT_INFO_PARSE_REQUIRED) == EDB_STMT_INFO_PARSE_REQUIRED) | ||
return info->query_id != UINT64CONST(0) ? info : NULL; | ||
|
||
info_str += info_len + 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The +1 makes me nervous about the case where there isn't a newline at the end of an info line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if the cases where there are untrusted entries in the query log is that likely, but this is C so I want to be extra careful about our boundary cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, good question. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because len is negative, at that point? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes exactly |
||
info_len = query_len - (int)(info_str - query_str); | ||
info_str = edbss_extract_info_line(info_str, &info_len); | ||
state.nested_level = 0; | ||
state.state = EDB_STMT_INFO_PARSE_NOOP; | ||
} | ||
edbss_free_stmt_info(info); | ||
} | ||
|
||
|
@@ -671,6 +683,13 @@ static JsonParseErrorType | |
edbss_json_scalar(void *semstate, char *token, JsonTokenType tokenType) { | ||
EdbStmtInfoSemState *state = (EdbStmtInfoSemState *) semstate; | ||
Assert(token != NULL); | ||
|
||
if (state->found & state->state) { | ||
pfree(token); | ||
state->state = EDB_STMT_INFO_PARSE_NOOP; | ||
return JSON_SUCCESS; | ||
} | ||
|
||
switch (state->state) { | ||
case EDB_STMT_INFO_PARSE_QUERY: | ||
if (tokenType == JSON_TOKEN_STRING) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the state get mutated even if there is an error? I guess that's probably fine?
What happens to
info_len
on an error? Is it still updated with however much got consumed?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it'll keep the parsed values and continue to the next line, which I think is fine.
On error,
info_len
will be updated to skip the whole failing line and restart on the next line.