diff --git a/db/helpers.go b/db/helpers.go index f4b8b61..70e61b9 100644 --- a/db/helpers.go +++ b/db/helpers.go @@ -3418,7 +3418,7 @@ SELECT "IsTemplate", MAX("PoweredOn") AS "PoweredOn", "SrmPlaceholder", "VmUuid", - SUM("SamplesPresent") AS "SamplesPresent", + CAST(SUM("SamplesPresent") AS BIGINT) AS "SamplesPresent", CASE WHEN totals.total_samples > 0 THEN SUM(CASE WHEN "AvgVcpuCount" IS NOT NULL THEN "AvgVcpuCount" * total_samples_day ELSE 0 END) / totals.total_samples ELSE NULL END AS "AvgVcpuCount", diff --git a/db/helpers_sql_builder_test.go b/db/helpers_sql_builder_test.go index 2d7ccbb..942f3be 100644 --- a/db/helpers_sql_builder_test.go +++ b/db/helpers_sql_builder_test.go @@ -22,3 +22,13 @@ GROUP BY`) { t.Fatalf("unexpected final GROUP BY after agg/totals join; this breaks Postgres SQLSTATE 42803") } } + +func TestBuildMonthlySummaryInsertCastsSampleSumToBigInt(t *testing.T) { + query, err := BuildMonthlySummaryInsert("inventory_monthly_summary_202601", "SELECT 1") + if err != nil { + t.Fatalf("BuildMonthlySummaryInsert failed: %v", err) + } + if !strings.Contains(query, `CAST(SUM("SamplesPresent") AS BIGINT) AS "SamplesPresent"`) { + t.Fatalf("expected monthly sample sum cast to BIGINT to avoid Postgres numeric assignment issues") + } +} diff --git a/internal/tasks/canonical_union_test.go b/internal/tasks/canonical_union_test.go new file mode 100644 index 0000000..0b38ca5 --- /dev/null +++ b/internal/tasks/canonical_union_test.go @@ -0,0 +1,39 @@ +package tasks + +import ( + "strings" + "testing" + "time" +) + +func TestBuildCanonicalHourlySummaryUnionCastsInventoryIDToBigInt(t *testing.T) { + start := time.Unix(1700000000, 0).UTC() + end := start.Add(24 * time.Hour) + + query := buildCanonicalHourlySummaryUnion(start, end) + if !strings.Contains(query, `CAST(NULL AS BIGINT) AS "InventoryId"`) { + t.Fatalf("expected InventoryId cast to BIGINT in canonical hourly union query") + } + if !strings.Contains(query, `CAST(NULL AS TEXT) AS "EventKey"`) { + t.Fatalf("expected EventKey cast to TEXT in canonical hourly union query") + } + if !strings.Contains(query, `CAST(NULL AS TEXT) AS "CloudId"`) { + t.Fatalf("expected CloudId cast to TEXT in canonical hourly union query") + } +} + +func TestBuildCanonicalDailyRollupSummaryUnionCastsInventoryIDToBigInt(t *testing.T) { + start := time.Unix(1700000000, 0).UTC() + end := start.AddDate(0, 1, 0) + + query := buildCanonicalDailyRollupSummaryUnion(start, end) + if !strings.Contains(query, `CAST(NULL AS BIGINT) AS "InventoryId"`) { + t.Fatalf("expected InventoryId cast to BIGINT in canonical daily rollup union query") + } + if !strings.Contains(query, `CAST(NULL AS TEXT) AS "EventKey"`) { + t.Fatalf("expected EventKey cast to TEXT in canonical daily rollup union query") + } + if !strings.Contains(query, `CAST(NULL AS TEXT) AS "CloudId"`) { + t.Fatalf("expected CloudId cast to TEXT in canonical daily rollup union query") + } +} diff --git a/internal/tasks/dailyAggregate.go b/internal/tasks/dailyAggregate.go index 8b81e0f..50b04b9 100644 --- a/internal/tasks/dailyAggregate.go +++ b/internal/tasks/dailyAggregate.go @@ -313,12 +313,12 @@ func (c *CronTask) aggregateDailySummarySQLCanonical(ctx context.Context, daySta func buildCanonicalHourlySummaryUnion(start, end time.Time) string { return fmt.Sprintf(` SELECT - NULL AS "InventoryId", + CAST(NULL AS BIGINT) AS "InventoryId", COALESCE("Name",'') AS "Name", COALESCE("Vcenter",'') AS "Vcenter", COALESCE("VmId",'') AS "VmId", - NULL AS "EventKey", - NULL AS "CloudId", + CAST(NULL AS TEXT) AS "EventKey", + CAST(NULL AS TEXT) AS "CloudId", COALESCE("CreationTime",0) AS "CreationTime", COALESCE("DeletionTime",0) AS "DeletionTime", COALESCE("ResourcePool",'') AS "ResourcePool", diff --git a/internal/tasks/monthlyAggregate.go b/internal/tasks/monthlyAggregate.go index 13ae511..e022512 100644 --- a/internal/tasks/monthlyAggregate.go +++ b/internal/tasks/monthlyAggregate.go @@ -769,12 +769,12 @@ WHERE "Date" >= %d AND "Date" < %d func buildCanonicalDailyRollupSummaryUnion(start, end time.Time) string { return fmt.Sprintf(` SELECT - NULL AS "InventoryId", + CAST(NULL AS BIGINT) AS "InventoryId", COALESCE("Name",'') AS "Name", COALESCE("Vcenter",'') AS "Vcenter", COALESCE("VmId",'') AS "VmId", - NULL AS "EventKey", - NULL AS "CloudId", + CAST(NULL AS TEXT) AS "EventKey", + CAST(NULL AS TEXT) AS "CloudId", COALESCE("CreationTime",0) AS "CreationTime", COALESCE("DeletionTime",0) AS "DeletionTime", COALESCE("LastResourcePool",'') AS "ResourcePool",