feat: grafana postgresql data source query builder support (#7379)

* feat: grafana postgresql data source query builder support

* test: add sqlness test cases
This commit is contained in:
Ning Sun
2025-12-11 11:18:35 +08:00
committed by GitHub
parent 1d5291b06d
commit 276f6bf026
7 changed files with 148 additions and 20 deletions

4
Cargo.lock generated
View File

@@ -3751,9 +3751,9 @@ dependencies = [
[[package]] [[package]]
name = "datafusion-pg-catalog" name = "datafusion-pg-catalog"
version = "0.12.2" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755393864c0c2dd95575ceed4b25e348686028e1b83d06f8f39914209999f821" checksum = "09bfd1feed7ed335227af0b65955ed825e467cf67fad6ecd089123202024cfd1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"datafusion", "datafusion",

View File

@@ -131,7 +131,7 @@ datafusion-functions = "50"
datafusion-functions-aggregate-common = "50" datafusion-functions-aggregate-common = "50"
datafusion-optimizer = "50" datafusion-optimizer = "50"
datafusion-orc = "0.5" datafusion-orc = "0.5"
datafusion-pg-catalog = "0.12.2" datafusion-pg-catalog = "0.12.3"
datafusion-physical-expr = "50" datafusion-physical-expr = "50"
datafusion-physical-plan = "50" datafusion-physical-plan = "50"
datafusion-sql = "50" datafusion-sql = "50"

20
flake.lock generated
View File

@@ -8,11 +8,11 @@
"rust-analyzer-src": "rust-analyzer-src" "rust-analyzer-src": "rust-analyzer-src"
}, },
"locked": { "locked": {
"lastModified": 1760078406, "lastModified": 1765252472,
"narHash": "sha256-JeJK0ZA845PtkCHkfo4KjeI1mYrsr2s3cxBYKhF4BoE=", "narHash": "sha256-byMt/uMi7DJ8tRniFopDFZMO3leSjGp6GS4zWOFT+uQ=",
"owner": "nix-community", "owner": "nix-community",
"repo": "fenix", "repo": "fenix",
"rev": "351277c60d104944122ee389cdf581c5ce2c6732", "rev": "8456b985f6652e3eef0632ee9992b439735c5544",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -41,16 +41,16 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1759994382, "lastModified": 1764983851,
"narHash": "sha256-wSK+3UkalDZRVHGCRikZ//CyZUJWDJkBDTQX1+G77Ow=", "narHash": "sha256-y7RPKl/jJ/KAP/VKLMghMgXTlvNIJMHKskl8/Uuar7o=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5da4a26309e796daa7ffca72df93dbe53b8164c7", "rev": "d9bc5c7dceb30d8d6fafa10aeb6aa8a48c218454",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-25.05", "ref": "nixos-25.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
@@ -65,11 +65,11 @@
"rust-analyzer-src": { "rust-analyzer-src": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1760014945, "lastModified": 1765120009,
"narHash": "sha256-ySdl7F9+oeWNHVrg3QL/brazqmJvYFEdpGnF3pyoDH8=", "narHash": "sha256-nG76b87rkaDzibWbnB5bYDm6a52b78A+fpm+03pqYIw=",
"owner": "rust-lang", "owner": "rust-lang",
"repo": "rust-analyzer", "repo": "rust-analyzer",
"rev": "90d2e1ce4dfe7dc49250a8b88a0f08ffdb9cb23f", "rev": "5e3e9c4e61bba8a5e72134b9ffefbef8f531d008",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -2,7 +2,7 @@
description = "Development environment flake"; description = "Development environment flake";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
fenix = { fenix = {
url = "github:nix-community/fenix"; url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -48,7 +48,7 @@
gnuplot ## for cargo bench gnuplot ## for cargo bench
]; ];
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; buildInputs = buildInputs;
NIX_HARDENING_ENABLE = ""; NIX_HARDENING_ENABLE = "";
}; };
}); });

View File

@@ -387,6 +387,8 @@ impl PGCatalogFunction {
registry.register(pg_catalog::create_pg_stat_get_numscans()); registry.register(pg_catalog::create_pg_stat_get_numscans());
registry.register(pg_catalog::create_pg_get_constraintdef()); registry.register(pg_catalog::create_pg_get_constraintdef());
registry.register(pg_catalog::create_pg_get_partition_ancestors_udf()); registry.register(pg_catalog::create_pg_get_partition_ancestors_udf());
registry.register(pg_catalog::quote_ident_udf::create_quote_ident_udf());
registry.register(pg_catalog::quote_ident_udf::create_parse_ident_udf());
registry.register_scalar(ObjDescriptionFunction::new()); registry.register_scalar(ObjDescriptionFunction::new());
registry.register_scalar(ColDescriptionFunction::new()); registry.register_scalar(ColDescriptionFunction::new());
registry.register_scalar(ShobjDescriptionFunction::new()); registry.register_scalar(ShobjDescriptionFunction::new());

View File

@@ -862,6 +862,77 @@ where relnamespace in (
| foo | | foo |
+---------+ +---------+
-- SQLNESS PROTOCOL POSTGRES
SELECT
CASE WHEN
quote_ident(table_schema) IN (
SELECT
CASE WHEN trim(s[i]) = '"$user"' THEN user ELSE trim(s[i]) END
FROM
generate_series(
array_lower(string_to_array(current_setting('search_path'),','),1),
array_upper(string_to_array(current_setting('search_path'),','),1)
) as i,
string_to_array(current_setting('search_path'),',') s
)
THEN quote_ident(table_name)
ELSE quote_ident(table_schema) || '.' || quote_ident(table_name)
END AS "table"
FROM information_schema.tables
WHERE quote_ident(table_schema) NOT IN ('information_schema',
'pg_catalog',
'_timescaledb_cache',
'_timescaledb_catalog',
'_timescaledb_internal',
'_timescaledb_config',
'timescaledb_information',
'timescaledb_experimental')
ORDER BY CASE WHEN
quote_ident(table_schema) IN (
SELECT
CASE WHEN trim(s[i]) = '"$user"' THEN user ELSE trim(s[i]) END
FROM
generate_series(
array_lower(string_to_array(current_setting('search_path'),','),1),
array_upper(string_to_array(current_setting('search_path'),','),1)
) as i,
string_to_array(current_setting('search_path'),',') s
) THEN 0 ELSE 1 END, 1;
+----------------+
| table |
+----------------+
| my_db.foo |
| public.numbers |
+----------------+
-- SQLNESS PROTOCOL POSTGRES
SELECT quote_ident(column_name) AS "column", data_type AS "type"
FROM information_schema.columns
WHERE
CASE WHEN array_length(parse_ident('my_db.foo'),1) = 2
THEN quote_ident(table_schema) = (parse_ident('my_db.foo'))[1]
AND quote_ident(table_name) = (parse_ident('my_db.foo'))[2]
ELSE quote_ident(table_name) = 'my_db.foo'
AND
quote_ident(table_schema) IN (
SELECT
CASE WHEN trim(s[i]) = '"$user"' THEN user ELSE trim(s[i]) END
FROM
generate_series(
array_lower(string_to_array(current_setting('search_path'),','),1),
array_upper(string_to_array(current_setting('search_path'),','),1)
) as i,
string_to_array(current_setting('search_path'),',') s
)
END;
+--------+--------------+
| column | type |
+--------+--------------+
| ts | timestamp(3) |
+--------+--------------+
-- SQLNESS PROTOCOL POSTGRES -- SQLNESS PROTOCOL POSTGRES
-- SQLNESS REPLACE (\d+\s*) OID -- SQLNESS REPLACE (\d+\s*) OID
select relnamespace, relname, relkind select relnamespace, relname, relkind
@@ -1013,9 +1084,7 @@ SELECT
oid oid
,nspname ,nspname
,nspname = ANY (current_schemas(true)) AS is_on_search_path ,nspname = ANY (current_schemas(true)) AS is_on_search_path
,obj_description(oid, 'pg_namespace') AS comment ,obj_description(oid, 'pg_namespace') AS comment
FROM pg_namespace; SELECT FROM pg_namespace; SELECT
oid oid
,nspname ,nspname

View File

@@ -132,6 +132,64 @@ where relnamespace in (
where nspname like 'my%' where nspname like 'my%'
); );
-- SQLNESS PROTOCOL POSTGRES
SELECT
CASE WHEN
quote_ident(table_schema) IN (
SELECT
CASE WHEN trim(s[i]) = '"$user"' THEN user ELSE trim(s[i]) END
FROM
generate_series(
array_lower(string_to_array(current_setting('search_path'),','),1),
array_upper(string_to_array(current_setting('search_path'),','),1)
) as i,
string_to_array(current_setting('search_path'),',') s
)
THEN quote_ident(table_name)
ELSE quote_ident(table_schema) || '.' || quote_ident(table_name)
END AS "table"
FROM information_schema.tables
WHERE quote_ident(table_schema) NOT IN ('information_schema',
'pg_catalog',
'_timescaledb_cache',
'_timescaledb_catalog',
'_timescaledb_internal',
'_timescaledb_config',
'timescaledb_information',
'timescaledb_experimental')
ORDER BY CASE WHEN
quote_ident(table_schema) IN (
SELECT
CASE WHEN trim(s[i]) = '"$user"' THEN user ELSE trim(s[i]) END
FROM
generate_series(
array_lower(string_to_array(current_setting('search_path'),','),1),
array_upper(string_to_array(current_setting('search_path'),','),1)
) as i,
string_to_array(current_setting('search_path'),',') s
) THEN 0 ELSE 1 END, 1;
-- SQLNESS PROTOCOL POSTGRES
SELECT quote_ident(column_name) AS "column", data_type AS "type"
FROM information_schema.columns
WHERE
CASE WHEN array_length(parse_ident('my_db.foo'),1) = 2
THEN quote_ident(table_schema) = (parse_ident('my_db.foo'))[1]
AND quote_ident(table_name) = (parse_ident('my_db.foo'))[2]
ELSE quote_ident(table_name) = 'my_db.foo'
AND
quote_ident(table_schema) IN (
SELECT
CASE WHEN trim(s[i]) = '"$user"' THEN user ELSE trim(s[i]) END
FROM
generate_series(
array_lower(string_to_array(current_setting('search_path'),','),1),
array_upper(string_to_array(current_setting('search_path'),','),1)
) as i,
string_to_array(current_setting('search_path'),',') s
)
END;
-- SQLNESS PROTOCOL POSTGRES -- SQLNESS PROTOCOL POSTGRES
-- SQLNESS REPLACE (\d+\s*) OID -- SQLNESS REPLACE (\d+\s*) OID
select relnamespace, relname, relkind select relnamespace, relname, relkind
@@ -192,12 +250,11 @@ SELECT
oid oid
,nspname ,nspname
,nspname = ANY (current_schemas(true)) AS is_on_search_path ,nspname = ANY (current_schemas(true)) AS is_on_search_path
,obj_description(oid, 'pg_namespace') AS comment ,obj_description(oid, 'pg_namespace') AS comment
FROM pg_namespace; SELECT FROM pg_namespace; SELECT
oid oid
,nspname ,nspname
FROM pg_namespace FROM pg_namespace
WHERE oid = pg_my_temp_schema(); WHERE oid = pg_my_temp_schema();