fix(ci): stabilize cross-platform workflow checks

This commit is contained in:
zhefox
2026-07-29 21:55:43 +08:00
parent 37df5b93b1
commit f8000012f7
40 changed files with 251 additions and 139 deletions
@@ -7,7 +7,7 @@ use tracing::info;
// Generated by build.rs from schema/bootstrap/postgres.
pub(crate) static EMPTY_DATABASE_SNAPSHOT_SQL: &str =
include_str!(concat!(env!("OUT_DIR"), "/empty_database_snapshot.sql"));
pub(crate) const EMPTY_DATABASE_SNAPSHOT_CUTOFF_VERSION: i64 = 20260720000000;
pub(crate) const EMPTY_DATABASE_SNAPSHOT_CUTOFF_VERSION: i64 = 20260727000000;
const PUBLIC_BASE_TABLE_COUNT_SQL: &str = r#"
SELECT COUNT(*)::BIGINT
@@ -536,6 +536,7 @@ const REQUEST_BODY_DETAIL_TABLES: &[&str] = &["usage_body_blobs"];
#[cfg(all(feature = "postgres", feature = "sqlite"))]
const LIFECYCLE_TABLES: &[&str] = &["_sqlx_migrations", "schema_backfills"];
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
fn import_column_stores_timestamp(column_name: &str) -> bool {
column_name.ends_with("_at")
|| column_name.ends_with("_unix_secs")
@@ -547,6 +548,7 @@ fn import_column_stores_timestamp(column_name: &str) -> bool {
)
}
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
fn import_timestamp_uses_millis(table_name: &str, column_name: &str) -> bool {
if !column_name.ends_with("_unix_ms") {
return false;
@@ -562,6 +564,7 @@ fn import_timestamp_uses_millis(table_name: &str, column_name: &str) -> bool {
!(relation_name == "usage" && column_name == "created_at_unix_ms")
}
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
fn normalize_imported_integer_timestamp(
driver_name: &str,
table_name: &str,
@@ -597,6 +600,7 @@ fn normalize_imported_integer_timestamp(
Ok(Some(timestamp))
}
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
fn parse_imported_datetime(value: &str) -> Option<chrono::DateTime<chrono::Utc>> {
let value = value.trim();
if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(value) {
@@ -476,9 +476,9 @@ async fn load_mysql_import_columns(
let rows = sqlx::query(
r#"
SELECT
COLUMN_NAME AS column_name,
DATA_TYPE AS data_type,
COLUMN_KEY AS column_key,
CAST(COLUMN_NAME AS CHAR) AS column_name,
CAST(DATA_TYPE AS CHAR) AS data_type,
CAST(COLUMN_KEY AS CHAR) AS column_key,
ORDINAL_POSITION AS ordinal_position
FROM information_schema.columns
WHERE table_schema = DATABASE()
@@ -506,7 +506,7 @@ WHERE table_schema = DATABASE()
.eq_ignore_ascii_case("PRI")
{
primary_key.insert(
row.try_get::<i64, _>("ordinal_position").map_sql_err()?,
row.try_get::<u32, _>("ordinal_position").map_sql_err()?,
name,
);
}
@@ -662,7 +662,9 @@ fn mysql_value_to_json(row: &sqlx::mysql::MySqlRow, index: usize) -> Result<Valu
})
}
"DECIMAL" | "NEWDECIMAL" => Ok(Value::String(
row.try_get::<String, _>(index).map_sql_err()?,
row.try_get::<sqlx::types::BigDecimal, _>(index)
.map_sql_err()?
.to_string(),
)),
"VARCHAR" | "VAR_STRING" | "STRING" | "TEXT" | "TINYTEXT" | "MEDIUMTEXT" | "LONGTEXT"
| "JSON" | "ENUM" | "SET" | "DATE" | "DATETIME" | "TIMESTAMP" | "TIME" => Ok(
@@ -488,6 +488,10 @@ fn normalize_postgres_import_value(
if is_postgres_boolean_column(target_column) {
return normalize_postgres_boolean_value(column_name, value);
}
if is_postgres_integer_column(target_column) && import_column_stores_timestamp(column_name) {
return normalize_imported_integer_timestamp("postgres", table_name, column_name, value)
.map(|timestamp| timestamp.map_or(Value::Null, |value| Value::Number(value.into())));
}
if is_postgres_timestamp_column(target_column) {
return normalize_postgres_timestamp_value(table_name, column_name, value);
}
@@ -512,6 +516,13 @@ pub(super) fn is_postgres_timestamp_column(target_column: &PostgresImportColumn)
) || matches!(target_column.udt_name.as_str(), "timestamptz" | "timestamp")
}
fn is_postgres_integer_column(target_column: &PostgresImportColumn) -> bool {
matches!(
target_column.data_type.as_str(),
"smallint" | "integer" | "bigint"
) || matches!(target_column.udt_name.as_str(), "int2" | "int4" | "int8")
}
fn is_postgres_json_column(target_column: &PostgresImportColumn) -> bool {
matches!(target_column.data_type.as_str(), "json" | "jsonb")
|| matches!(target_column.udt_name.as_str(), "json" | "jsonb")
@@ -655,20 +666,6 @@ fn postgres_billing_table_name(
}
}
#[cfg(test)]
mod billing_table_tests {
use super::postgres_billing_table_name;
#[test]
fn settlement_snapshot_import_uses_request_id_conflict_key() {
assert_eq!(
postgres_billing_table_name("usage_settlement_snapshots")
.expect("settlement snapshot table should be supported"),
("public.usage_settlement_snapshots", "request_id")
);
}
}
async fn import_postgres_wallet_row(
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
row: &ExportRow,
@@ -721,3 +718,17 @@ fn postgres_wallet_table_name(
))
})
}
#[cfg(test)]
mod billing_table_tests {
use super::postgres_billing_table_name;
#[test]
fn settlement_snapshot_import_uses_request_id_conflict_key() {
assert_eq!(
postgres_billing_table_name("usage_settlement_snapshots")
.expect("settlement snapshot table should be supported"),
("public.usage_settlement_snapshots", "request_id")
);
}
}
@@ -290,6 +290,23 @@ fn cross_driver_timestamp_normalization_preserves_usage_second_contract() {
normalized["created_at_unix_ms"],
json!("2023-11-14T22:13:20+00:00")
);
let target_columns = BTreeMap::from([(
"created_at_unix_ms".to_string(),
postgres_column("bigint", "int8"),
)]);
let row = ExportRow {
id: "usage-1".to_string(),
payload: json!({ "created_at_unix_ms": "1970-01-01T00:00:01.234900Z" }),
};
let normalized = normalize_postgres_import_payload(
"public.usage",
ExportDomain::Usage,
&row,
&target_columns,
)
.expect("postgres integer usage timestamp should normalize");
assert_eq!(normalized["created_at_unix_ms"], json!(1));
}
#[test]
@@ -409,6 +409,7 @@ fn empty_database_snapshot_covers_current_cutoff_versions() {
20260718000000,
20260718010000,
20260720000000,
20260727000000,
]
);
}
@@ -1023,6 +1024,7 @@ fn mysql_and_sqlite_migrations_include_enabled_incrementals() {
20260725010000,
20260725020000,
20260725030000,
20260727000000,
]
);
assert_eq!(
@@ -1055,6 +1057,7 @@ fn mysql_and_sqlite_migrations_include_enabled_incrementals() {
20260725020000,
20260725030000,
20260725040000,
20260727000000,
]
);
}