test: add Parquet footer compatibility coverage

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
This commit is contained in:
jeremyhi
2026-08-06 19:59:50 +08:00
parent 222bfb74aa
commit a6a679aa68
6 changed files with 128 additions and 1 deletions
+38
View File
@@ -37,6 +37,7 @@ from pathlib import Path
VERSION_RE = re.compile(r"v[0-9]+\.[0-9]+\.[0-9]+")
PARQUET_FOOTER_CASE_FILTER = "^parquet_copy_footer_compatibility$"
def parse_args() -> argparse.Namespace:
@@ -175,6 +176,22 @@ def run_for_version(
preserve_state=preserve_state,
)
parquet_base_command = [
*base_command,
"--topology",
"standalone",
"--test-filter",
PARQUET_FOOTER_CASE_FILTER,
"--expect-cases",
"1",
]
run_compat(
base_command=parquet_base_command,
preview_title=f"Preview {from_version} -> current (standalone Parquet footer)",
compatibility_title=f"Compatibility {from_version} -> current (standalone Parquet footer)",
preserve_state=preserve_state,
)
def run_downgrade_for_version(
*,
@@ -206,6 +223,27 @@ def run_downgrade_for_version(
preserve_state=preserve_state,
)
parquet_base_command = [
str(runner),
"compat",
"--from-bins-dir",
str(current_bins_dir),
"--to-version",
to_version,
"--topology",
"standalone",
"--test-filter",
PARQUET_FOOTER_CASE_FILTER,
"--expect-cases",
"1",
]
run_compat(
base_command=parquet_base_command,
preview_title=f"Preview current -> {to_version} (standalone Parquet footer)",
compatibility_title=f"Compatibility current -> {to_version} (standalone Parquet footer)",
preserve_state=preserve_state,
)
def main() -> int:
args = parse_args()
@@ -0,0 +1,9 @@
name = "parquet_copy_footer_compatibility"
reason = "Verify both directions: current imports legacy Parquet without greptime.table_name, and v1.1.4 imports current Parquet with the footer key."
introduced_by = "PR #8766"
topologies = ["standalone"]
from_range = ["*"]
to_range = ["*"]
features = ["copy", "parquet"]
owner = "query"
namespace = "parquet_copy_footer_compatibility"
@@ -0,0 +1,11 @@
CREATE TABLE parquet_copy_footer_source(
ts TIMESTAMP TIME INDEX,
host STRING PRIMARY KEY,
val INT
);
INSERT INTO parquet_copy_footer_source VALUES
('2024-02-09 00:00:00+0000', 'host_a', 1),
('2024-02-09 00:01:00+0000', 'host_b', 2);
COPY parquet_copy_footer_source TO '${SQLNESS_HOME}/parquet_copy_footer_compatibility/source.parquet' WITH (format='parquet');
@@ -0,0 +1,20 @@
CREATE TABLE parquet_copy_footer_target(
ts TIMESTAMP TIME INDEX,
host STRING PRIMARY KEY,
val INT
);
Affected Rows: 0
COPY parquet_copy_footer_target FROM '${SQLNESS_HOME}/parquet_copy_footer_compatibility/source.parquet' WITH (format='parquet');
Affected Rows: 2
SELECT ts, host, val FROM parquet_copy_footer_target ORDER BY ts, host;
+---------------------+--------+-----+
| ts | host | val |
+---------------------+--------+-----+
| 2024-02-09T00:00:00 | host_a | 1 |
| 2024-02-09T00:01:00 | host_b | 2 |
+---------------------+--------+-----+
@@ -0,0 +1,9 @@
CREATE TABLE parquet_copy_footer_target(
ts TIMESTAMP TIME INDEX,
host STRING PRIMARY KEY,
val INT
);
COPY parquet_copy_footer_target FROM '${SQLNESS_HOME}/parquet_copy_footer_compatibility/source.parquet' WITH (format='parquet');
SELECT ts, host, val FROM parquet_copy_footer_target ORDER BY ts, host;
+41 -1
View File
@@ -29,6 +29,7 @@ use crate::util;
const COMMENT_PREFIX: &str = "--";
const INTERCEPTOR_PREFIX: &str = "-- SQLNESS";
const QUERY_DELIMITER: char = ';';
const SQLNESS_HOME_TEMPLATE: &str = "${SQLNESS_HOME}";
#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
enum CompatTopology {
@@ -564,6 +565,13 @@ enum CompatPhase {
Verify,
}
fn expand_sqlness_home(sql: &str, sqlness_home: Option<&str>) -> String {
match sqlness_home {
Some(sqlness_home) => sql.replace(SQLNESS_HOME_TEMPLATE, sqlness_home),
None => sql.to_string(),
}
}
/// Create an interceptor registry matching the ordinary sqlness runner.
fn create_interceptor_registry() -> Registry {
let mut interceptor_registry: Registry = Default::default();
@@ -851,6 +859,8 @@ impl ParsedStatement {
db.compat_prepare_query_context(&context).await;
run_namespace_prelude(db, namespace, &context).await?;
let sql = self.concat_query_lines();
let sqlness_home = std::env::var("SQLNESS_HOME").ok();
let sql = expand_sqlness_home(&sql, sqlness_home.as_deref());
let mut results = Vec::new();
for sql in sql.split(TEMPLATE_DELIMITER) {
@@ -942,7 +952,37 @@ fn simple_diff(expected: &str, actual: &str) -> String {
#[cfg(test)]
mod tests {
use super::trim_trailing_blank_lines;
use super::{ParsedStatement, expand_sqlness_home, trim_trailing_blank_lines};
#[test]
fn test_expand_sqlness_home_for_execution_only() {
let mut statement = ParsedStatement::new();
statement.append_query_line(
"COPY source TO '${SQLNESS_HOME}/compat/source.parquet' WITH (format='parquet');",
);
let execution_sql = expand_sqlness_home(
&statement.concat_query_lines(),
Some("/tmp/sqlness-compat/copy"),
);
assert_eq!(
execution_sql,
"COPY source TO '/tmp/sqlness-compat/copy/compat/source.parquet' WITH (format='parquet');"
);
assert_eq!(
statement.display_text(),
"COPY source TO '${SQLNESS_HOME}/compat/source.parquet' WITH (format='parquet');\n\n"
);
}
#[test]
fn test_expand_sqlness_home_without_value_leaves_execution_sql_unchanged() {
let sql =
"COPY source FROM '${SQLNESS_HOME}/compat/source.parquet' WITH (format='parquet');";
assert_eq!(expand_sqlness_home(sql, None), sql);
}
#[test]
fn test_trim_trailing_blank_lines_preserves_single_final_newline() {