Skip to content

Commit ce00a7e

Browse files
authored
Helper function handling: Fix detection of functions without arguments (#695)
This accidentally broke in a recent refactoring, and caused helpers with no arguments (e.g. "get_column_stats") to not be detected correctly. In passing refactor definitions of "get_column_stats" and "get_relation_stats_ext" helpers to be in SQL files.
1 parent 74bd365 commit ce00a7e

7 files changed

+49
-33
lines changed

Diff for: input/postgres/collection.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@ func (c *Collection) findHelperFunction(name string, inputTypes []string) (state
104104
return state.PostgresFunction{}, false
105105
}
106106
for _, f := range funcs {
107-
args := strings.Split(f.Arguments, ", ")
107+
var args []string
108+
if f.Arguments != "" {
109+
args = strings.Split(f.Arguments, ", ")
110+
}
108111
if len(inputTypes) > len(args) {
109112
// We're expecting more arguments than the function has
110113
continue

Diff for: input/postgres/collection_test.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,20 @@ var collectionHelperTests = []collectionHelperTestpair{
5050
false,
5151
"",
5252
},
53-
// Verify we allow default arguments to be ommitted
53+
// Verify we allow default arguments to be omitted
5454
{
5555
"get_stat_statements",
5656
[]string{},
5757
true,
5858
"SETOF pg_stat_statements",
5959
},
60+
// Verify functions with no arguments match as expected
61+
{
62+
"get_column_stats",
63+
[]string{},
64+
true,
65+
"TABLE(schemaname name, tablename name, attname name, inherited boolean, null_frac real, avg_width integer, n_distinct real, correlation real)",
66+
},
6067
}
6168

6269
func TestCollectionFindHelper(t *testing.T) {
@@ -73,6 +80,11 @@ func TestCollectionFindHelper(t *testing.T) {
7380
t.Fatalf("Could not load get_stat_statements helper: %s", err)
7481
}
7582

83+
_, err = db.Exec(util.GetColumnStatsHelper)
84+
if err != nil {
85+
t.Fatalf("Could not load get_column_stats helper: %s", err)
86+
}
87+
7688
ctx := context.Background()
7789
logger := &util.Logger{}
7890
server := &state.Server{}

Diff for: runner/generate_helper_sql.go

+2-30
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,6 @@ import (
1111
"github.com/pganalyze/collector/util"
1212
)
1313

14-
var statsHelpers = []string{
15-
// Column stats
16-
`DROP FUNCTION IF EXISTS pganalyze.get_column_stats;
17-
CREATE FUNCTION pganalyze.get_column_stats() RETURNS TABLE(
18-
schemaname name, tablename name, attname name, inherited bool, null_frac real, avg_width int, n_distinct real, correlation real
19-
) AS $$
20-
/* pganalyze-collector */
21-
SELECT schemaname, tablename, attname, inherited, null_frac, avg_width, n_distinct, correlation
22-
FROM pg_catalog.pg_stats
23-
WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND tablename <> 'pg_subscription';
24-
$$ LANGUAGE sql VOLATILE SECURITY DEFINER;`,
25-
26-
// Extended stats
27-
`DROP FUNCTION IF EXISTS pganalyze.get_relation_stats_ext;
28-
CREATE FUNCTION pganalyze.get_relation_stats_ext() RETURNS TABLE(
29-
statistics_schemaname text, statistics_name text,
30-
inherited boolean, n_distinct pg_ndistinct, dependencies pg_dependencies,
31-
most_common_val_nulls boolean[], most_common_freqs float8[], most_common_base_freqs float8[]
32-
) AS
33-
$$
34-
/* pganalyze-collector */ SELECT statistics_schemaname::text, statistics_name::text,
35-
(row_to_json(se.*)::jsonb ->> 'inherited')::boolean AS inherited, n_distinct, dependencies,
36-
most_common_val_nulls, most_common_freqs, most_common_base_freqs
37-
FROM pg_catalog.pg_stats_ext se
38-
WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND tablename <> 'pg_subscription';
39-
$$ LANGUAGE sql VOLATILE SECURITY DEFINER;`}
40-
4114
func GenerateStatsHelperSql(ctx context.Context, server *state.Server, opts state.CollectionOpts, logger *util.Logger) (string, error) {
4215
db, err := postgres.EstablishConnection(ctx, server, logger, opts, "")
4316
if err != nil {
@@ -55,9 +28,8 @@ func GenerateStatsHelperSql(ctx context.Context, server *state.Server, opts stat
5528
output.WriteString(fmt.Sprintf("\\c %s\n", pq.QuoteIdentifier(dbName)))
5629
output.WriteString("CREATE SCHEMA IF NOT EXISTS pganalyze;\n")
5730
output.WriteString(fmt.Sprintf("GRANT USAGE ON SCHEMA pganalyze TO %s;\n", server.Config.GetDbUsername()))
58-
for _, helper := range statsHelpers {
59-
output.WriteString(helper + "\n")
60-
}
31+
output.WriteString(util.GetColumnStatsHelper + "\n")
32+
output.WriteString(util.GetRelationStatsExtHelper + "\n")
6133
output.WriteString("\n")
6234
}
6335

Diff for: util/helpers/get_column_stats.sql

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
DROP FUNCTION IF EXISTS pganalyze.get_column_stats;
2+
CREATE FUNCTION pganalyze.get_column_stats() RETURNS TABLE(
3+
schemaname name, tablename name, attname name, inherited bool, null_frac real, avg_width int, n_distinct real, correlation real
4+
) AS $$
5+
/* pganalyze-collector */
6+
SELECT schemaname, tablename, attname, inherited, null_frac, avg_width, n_distinct, correlation
7+
FROM pg_catalog.pg_stats
8+
WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND tablename <> 'pg_subscription';
9+
$$ LANGUAGE sql VOLATILE SECURITY DEFINER;

Diff for: util/helpers/get_relation_stats_ext.sql

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
DROP FUNCTION IF EXISTS pganalyze.get_relation_stats_ext;
2+
CREATE FUNCTION pganalyze.get_relation_stats_ext() RETURNS TABLE(
3+
statistics_schemaname text, statistics_name text,
4+
inherited boolean, n_distinct pg_ndistinct, dependencies pg_dependencies,
5+
most_common_val_nulls boolean[], most_common_freqs float8[], most_common_base_freqs float8[]
6+
) AS
7+
$$
8+
/* pganalyze-collector */ SELECT statistics_schemaname::text, statistics_name::text,
9+
(row_to_json(se.*)::jsonb ->> 'inherited')::boolean AS inherited, n_distinct, dependencies,
10+
most_common_val_nulls, most_common_freqs, most_common_base_freqs
11+
FROM pg_catalog.pg_stats_ext se
12+
WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND tablename <> 'pg_subscription';
13+
$$ LANGUAGE sql VOLATILE SECURITY DEFINER;

Diff for: util/helpers/get_stat_statements.sql

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
CREATE OR REPLACE FUNCTION pganalyze.get_stat_statements(showtext boolean = true) RETURNS SETOF pg_stat_statements AS
1+
DROP FUNCTION IF EXISTS pganalyze.get_stat_statements;
2+
CREATE FUNCTION pganalyze.get_stat_statements(showtext boolean = true) RETURNS SETOF pg_stat_statements AS
23
$$
34
/* pganalyze-collector */ SELECT * FROM public.pg_stat_statements(showtext);
45
$$ LANGUAGE sql VOLATILE SECURITY DEFINER;

Diff for: util/sql_helpers.go

+6
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ var ExplainAnalyzeHelper string
99

1010
//go:embed helpers/get_stat_statements.sql
1111
var GetStatStatementsHelper string
12+
13+
//go:embed helpers/get_column_stats.sql
14+
var GetColumnStatsHelper string
15+
16+
//go:embed helpers/get_relation_stats_ext.sql
17+
var GetRelationStatsExtHelper string

0 commit comments

Comments
 (0)