Skip to content
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

Improve processing of nested queries #577

Merged
merged 3 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions pkg/eval_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,9 @@ func (q *EvalQuery) _columns(key, value, beforeMacrosQuery, fromQuery string) (s
var orderByQuery = " ORDER BY t, " + keyAlias
var havingQuery = ""
if matched, err := regexp.MatchString(`(?mi)^\s*FROM\s*\(`, fromQuery); err == nil && !matched {
var groupByIndex = strings.Index(strings.ToLower(fromQuery), "group by")
var havingIndex = strings.Index(strings.ToLower(fromQuery), "having")
var orderByIndex = strings.Index(strings.ToLower(fromQuery), "order by")
var groupByIndex = findKeywordOutsideBrackets(strings.ToLower(fromQuery), "group by")
var havingIndex = findKeywordOutsideBrackets(strings.ToLower(fromQuery), "having")
var orderByIndex = findKeywordOutsideBrackets(strings.ToLower(fromQuery), "order by")

if havingIndex >= 0 && orderByIndex >= 0 && havingIndex >= orderByIndex {
return "", fmt.Errorf("ORDER BY clause shall be before HAVING")
Expand Down Expand Up @@ -393,6 +393,34 @@ func (q *EvalQuery) _columns(key, value, beforeMacrosQuery, fromQuery string) (s
" ORDER BY t", nil
}

func findKeywordOutsideBrackets(query, keyword string) int {
bracketDepth := 0
keywordRegex := regexp.MustCompile("(?i)" + regexp.QuoteMeta(keyword)) // Case-insensitive match

for i := 0; i < len(query); i++ {
switch query[i] {
case '(':
bracketDepth++
case ')':
if bracketDepth > 0 {
bracketDepth--
}
default:
if bracketDepth == 0 {
// Check if the current segment matches the keyword
if keywordRegex.MatchString(query[i:]) {
// Check if this match is really starting at i
if matchIndexes := keywordRegex.FindStringIndex(query[i:]); matchIndexes != nil && matchIndexes[0] == 0 {
return i // Match found at index i
}
}
}
}
}

return -1 // No match found
}

func (q *EvalQuery) rateColumns(query string, ast *EvalAST) (string, error) {
macroQueries, err := q._parseMacro("$rateColumns", query)
if err != nil {
Expand Down
18 changes: 15 additions & 3 deletions src/datasource/sql-query/sql-query-macros.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,21 @@ export default class SqlQueryMacros {
let orderByQuery = ' ORDER BY t, ' + keyAlias;
const fromRe = /^\s*FROM\s*\(/mi;
if (!fromRe.test(fromQuery)) {
const groupByIndex = fromQuery.toLowerCase().indexOf('group by');
const havingIndex = fromQuery.toLowerCase().indexOf('having');
const orderByIndex = fromQuery.toLowerCase().indexOf('order by');

function findKeywordOutsideBrackets(query, keyword) {
// This regex will match the keyword only if it is not within brackets.
const regex = new RegExp(
`(?<!\\([^)]*)${keyword}(?![^(]*\\))`,
'gi'
);

const match = regex.exec(query);
return match ? match.index : -1;
}

const groupByIndex = findKeywordOutsideBrackets(fromQuery, 'group by')
const havingIndex = findKeywordOutsideBrackets(fromQuery, 'having')
const orderByIndex = findKeywordOutsideBrackets(fromQuery, 'order by')

if (havingIndex >= 0 && orderByIndex >= 0 && havingIndex >= orderByIndex) {
throw {message: "ORDER BY clause shall be before HAVING"};
Expand Down
Loading