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]]
name = "datafusion-pg-catalog"
version = "0.12.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755393864c0c2dd95575ceed4b25e348686028e1b83d06f8f39914209999f821"
checksum = "09bfd1feed7ed335227af0b65955ed825e467cf67fad6ecd089123202024cfd1"
dependencies = [
"async-trait",
"datafusion",

View File

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

20
flake.lock generated
View File

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

View File

@@ -2,7 +2,7 @@
description = "Development environment flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
@@ -48,7 +48,7 @@
gnuplot ## for cargo bench
];
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
buildInputs = buildInputs;
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_get_constraintdef());
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(ColDescriptionFunction::new());
registry.register_scalar(ShobjDescriptionFunction::new());

View File

@@ -862,6 +862,77 @@ where relnamespace in (
| 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 REPLACE (\d+\s*) OID
select relnamespace, relname, relkind
@@ -1013,9 +1084,7 @@ SELECT
oid
,nspname
,nspname = ANY (current_schemas(true)) AS is_on_search_path
,obj_description(oid, 'pg_namespace') AS comment
FROM pg_namespace; SELECT
oid
,nspname

View File

@@ -132,6 +132,64 @@ where relnamespace in (
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 REPLACE (\d+\s*) OID
select relnamespace, relname, relkind
@@ -192,12 +250,11 @@ SELECT
oid
,nspname
,nspname = ANY (current_schemas(true)) AS is_on_search_path
,obj_description(oid, 'pg_namespace') AS comment
FROM pg_namespace; SELECT
oid
,nspname
FROM pg_namespace
WHERE oid = pg_my_temp_schema();