SQL Expressions: Re-implement feature using go-mysql-server (#99521)

* Under feature flag `sqlExpressions` and is experimental
* Excluded from arm32
* Will not work with the Query Service yet
* Does not have limits in place yet
* Does not working with alerting yet
* Currently requires "prepare time series" Transform for time series viz
 
---------

Co-authored-by: Sam Jewell <sam.jewell@grafana.com>
This commit is contained in:
Kyle Brandt
2025-02-06 07:27:28 -05:00
committed by GitHub
parent 4e6bdce41c
commit d64f41afdc
33 changed files with 1969 additions and 405 deletions

View File

@ -3,214 +3,131 @@ package sql
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParse(t *testing.T) {
t.Skip()
sql := "select * from foo"
tables, err := TablesList((sql))
assert.Nil(t, err)
func TestTablesList(t *testing.T) {
tests := []struct {
name string
sql string
expected []string
expectError bool
}{
{
name: "simple select",
sql: "select * from foo",
expected: []string{"foo"},
},
{
name: "select with comma",
sql: "select * from foo,bar",
expected: []string{"bar", "foo"},
},
{
name: "select with multiple commas",
sql: "select * from foo,bar,baz",
expected: []string{"bar", "baz", "foo"},
},
{
name: "no table",
sql: "select 1 as 'n'",
expected: []string{},
},
{
name: "json array",
sql: "SELECT JSON_ARRAY(1, 2, 3) AS array_value",
expected: []string{},
},
{
name: "json extract",
sql: "SELECT JSON_EXTRACT(JSON_ARRAY(1, 2, 3), '$[0]') AS first_element;",
expected: []string{},
},
{
name: "json int array",
sql: "SELECT JSON_ARRAY(3, 2, 1) AS int_array;",
expected: []string{},
},
{
name: "subquery",
sql: "select * from (select * from people limit 1) AS subquery",
expected: []string{"people"},
},
{
name: "join",
sql: `select * from A
JOIN B ON A.name = B.name
LIMIT 10`,
expected: []string{"A", "B"},
},
{
name: "right join",
sql: `select * from A
RIGHT JOIN B ON A.name = B.name
LIMIT 10`,
expected: []string{"A", "B"},
},
{
name: "alias with join",
sql: `select * from A as X
RIGHT JOIN B ON A.name = X.name
LIMIT 10`,
expected: []string{"A", "B"},
},
{
name: "alias",
sql: "select * from A as X LIMIT 10",
expected: []string{"A"},
},
{
name: "error case",
sql: "select * from zzz aaa zzz",
expectError: true,
},
{
name: "parens",
sql: `SELECT t1.Col1,
t2.Col1,
t3.Col1
FROM table1 AS t1
LEFT JOIN (
table2 AS t2
INNER JOIN table3 AS t3 ON t3.Col1 = t2.Col1
) ON t2.Col1 = t1.Col1;`,
expected: []string{"table1", "table2", "table3"},
},
{
name: "with clause",
sql: `WITH top_products AS (
SELECT * FROM products
ORDER BY price DESC
LIMIT 5
)
SELECT name, price
FROM top_products;`,
expected: []string{"products", "top_products"},
},
{
name: "with quote",
sql: "select *,'junk' from foo",
expected: []string{"foo"},
},
{
name: "with quote 2",
sql: "SELECT json_serialize_sql('SELECT 1')",
expected: []string{},
},
}
assert.Equal(t, "foo", tables[0])
}
func TestParseWithComma(t *testing.T) {
t.Skip()
sql := "select * from foo,bar"
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, "bar", tables[0])
assert.Equal(t, "foo", tables[1])
}
func TestParseWithCommas(t *testing.T) {
t.Skip()
sql := "select * from foo,bar,baz"
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, "bar", tables[0])
assert.Equal(t, "baz", tables[1])
assert.Equal(t, "foo", tables[2])
}
func TestArray(t *testing.T) {
t.Skip()
sql := "SELECT array_value(1, 2, 3)"
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 0, len(tables))
}
func TestArray2(t *testing.T) {
t.Skip()
sql := "SELECT array_value(1, 2, 3)[2]"
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 0, len(tables))
}
func TestXxx(t *testing.T) {
t.Skip()
sql := "SELECT [3, 2, 1]::INT[3];"
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 0, len(tables))
}
func TestParseSubquery(t *testing.T) {
t.Skip()
sql := "select * from (select * from people limit 1)"
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 1, len(tables))
assert.Equal(t, "people", tables[0])
}
func TestJoin(t *testing.T) {
t.Skip()
sql := `select * from A
JOIN B ON A.name = B.name
LIMIT 10`
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 2, len(tables))
assert.Equal(t, "A", tables[0])
assert.Equal(t, "B", tables[1])
}
func TestRightJoin(t *testing.T) {
t.Skip()
sql := `select * from A
RIGHT JOIN B ON A.name = B.name
LIMIT 10`
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 2, len(tables))
assert.Equal(t, "A", tables[0])
assert.Equal(t, "B", tables[1])
}
func TestAliasWithJoin(t *testing.T) {
t.Skip()
sql := `select * from A as X
RIGHT JOIN B ON A.name = X.name
LIMIT 10`
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 2, len(tables))
assert.Equal(t, "A", tables[0])
assert.Equal(t, "B", tables[1])
}
func TestAlias(t *testing.T) {
t.Skip()
sql := `select * from A as X LIMIT 10`
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 1, len(tables))
assert.Equal(t, "A", tables[0])
}
func TestError(t *testing.T) {
t.Skip()
sql := `select * from zzz aaa zzz`
_, err := TablesList((sql))
assert.NotNil(t, err)
}
func TestParens(t *testing.T) {
t.Skip()
sql := `SELECT t1.Col1,
t2.Col1,
t3.Col1
FROM table1 AS t1
LEFT JOIN (
table2 AS t2
INNER JOIN table3 AS t3 ON t3.Col1 = t2.Col1
) ON t2.Col1 = t1.Col1;`
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 3, len(tables))
assert.Equal(t, "table1", tables[0])
assert.Equal(t, "table2", tables[1])
assert.Equal(t, "table3", tables[2])
}
func TestWith(t *testing.T) {
t.Skip()
sql := `WITH
current_month AS (
select
distinct "Month(ISO)" as mth
from A
ORDER BY mth DESC
LIMIT 1
),
last_month_bill AS (
select
CAST (
sum(
CAST(BillableSeries AS INTEGER)
) AS INTEGER
) AS BillableSeries,
"Month(ISO)",
label_namespace
-- , B.activeseries_count
from A
JOIN current_month
ON current_month.mth = A."Month(ISO)"
JOIN B
ON B.namespace = A.label_namespace
GROUP BY
label_namespace,
"Month(ISO)"
ORDER BY BillableSeries DESC
)
SELECT
last_month_bill.*,
BEE.activeseries_count
FROM last_month_bill
JOIN BEE
ON BEE.namespace = last_month_bill.label_namespace`
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 5, len(tables))
assert.Equal(t, "A", tables[0])
assert.Equal(t, "B", tables[1])
assert.Equal(t, "BEE", tables[2])
}
func TestWithQuote(t *testing.T) {
t.Skip()
sql := "select *,'junk' from foo"
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, "foo", tables[0])
}
func TestWithQuote2(t *testing.T) {
t.Skip()
sql := "SELECT json_serialize_sql('SELECT 1')"
tables, err := TablesList((sql))
assert.Nil(t, err)
assert.Equal(t, 0, len(tables))
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
tables, err := TablesList(tc.sql)
if tc.expectError {
require.NotNil(t, err, "expected error for SQL: %s", tc.sql)
} else {
require.Nil(t, err, "unexpected error for SQL: %s", tc.sql)
require.Equal(t, tc.expected, tables, "mismatched tables for SQL: %s", tc.sql)
}
})
}
}