Skip to content

Comments

Partitions#3276

Draft
neisw wants to merge 2 commits intoopenshift:mainfrom
neisw:partitions
Draft

Partitions#3276
neisw wants to merge 2 commits intoopenshift:mainfrom
neisw:partitions

Conversation

@neisw
Copy link
Contributor

@neisw neisw commented Feb 18, 2026

Initial work managing tables and partitions within sippy

Summary by CodeRabbit

  • New Features

    • Added partition lifecycle management with automated aging and preparation of daily partitions.
    • Added database utilities for schema verification and data migration across tables.
    • Added comprehensive partition management APIs supporting creation, deletion, detachment, and retention policies.
  • Documentation

    • Added extensive documentation for partition management and database utilities APIs.
    • Added example workflows demonstrating migration, verification, and partition operations.
  • Tests

    • Added test coverage for partition naming, configuration validation, and data type mappings.

@openshift-ci-robot
Copy link

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: automatic mode

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Walkthrough

Introduces comprehensive partition lifecycle management for PostgreSQL and database utilities for schema verification, data migration, and partition coverage validation. Integrates partition management into the prowloader workflow and provides extensive APIs with documentation and examples.

Changes

Cohort / File(s) Summary
Partition Management Core APIs
pkg/db/partitions/partitions.go
Introduces 28+ public functions and 5 exported types for comprehensive partition operations: configuration (RANGE, LIST, HASH strategies), metadata retrieval (listing, stats, age grouping), lifecycle management (creation, attachment, detachment, dropping with dry-run support), and utilities for type mapping and partition naming.
Partition Management Tests & Examples
pkg/db/partitions/partitions_test.go, pkg/db/partitions/examples.go
Adds 542 lines of partition naming and configuration validation tests; introduces 18 example functions demonstrating partition listing, stats analysis, retention policies, cleanup workflows, and complete lifecycle operations.
Partition Management Documentation
pkg/db/partitions/README.md
Comprehensive 1724-line documentation covering read/write operations, safety features, error handling, usage workflows, lifecycle examples, integration guidance, and best practices for partition management.
Database Utilities Core APIs
pkg/db/utils.go
Introduces 11 new methods and 3 exported types (ColumnInfo, PartitionStrategy, ColumnVerificationOptions) for table schema verification, row counting, full/range-based data migration, partition coverage validation, and identity column synchronization.
Database Utilities Tests & Examples
pkg/db/utils_test.go, pkg/db/utils_example.go
Adds 303 lines of unit tests for type normalization and data structures; provides 17 example functions demonstrating schema verification, migration workflows (batch, backup, range-based), partition coverage checks, and identity syncing.
Database Utilities Documentation
pkg/db/UTILS_README.md
Comprehensive 836-line documentation detailing schema verification, migration utilities, partition management integration, type normalization rules, error handling expectations, and scenario-based workflows with code examples.
Prowloader Partition Integration
pkg/dataloader/prowloader/prow.go
Integrates partition lifecycle management into daily test analysis loading workflow: adds aging of old partitions and preparation of new partitions (48-hour horizon) for test_analysis_by_job_by_dates table with error handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 inconclusive)

Check name Status Explanation Resolution
Sql Injection Prevention ❌ Error SQL injection vulnerabilities detected: unquoted identifiers directly interpolated into SQL statements without parameterization or validation. Apply pq.QuoteIdentifier() to all table, column, index, and partition names in SQL queries. Import github.com/lib/pq in partitions.go and utils.go.
Title check ❓ Inconclusive The title 'Partitions' is vague and generic, failing to convey meaningful information about the changeset despite substantial additions to partition management infrastructure. Use a more descriptive title that captures the main change, such as 'Add partition lifecycle management APIs and utilities for daily partitioned tables' or 'Implement PostgreSQL partition management framework'.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 80.68% which is sufficient. The required threshold is 80.00%.
Go Error Handling ✅ Passed Go error handling patterns properly check errors with if err != nil statements throughout the code, with no ignored errors or panic calls outside init functions detected.
Excessive Css In React Should Use Styles ✅ Passed This custom check is not applicable to this pull request as it contains only Go backend code and documentation, with no React components or CSS styling patterns.
Single Responsibility And Clear Naming ✅ Passed Pull request maintains single responsibility principles with cohesive partitions package and focused utils module; naming is clear and descriptive; struct field counts (4-8 fields) and function parameters are reasonable.
✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 18, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 18, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@petr-muller
Copy link
Member

/cc

@openshift-ci openshift-ci bot requested a review from petr-muller February 18, 2026 12:42
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (3)
pkg/dataloader/prowloader/prow.go (1)

361-374: Confirm the hard‑coded retention windows.

The 90‑day detach + 100‑day drop directly controls data deletion. Please confirm this matches the desired retention policy; if it should vary by environment, consider making it configurable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/dataloader/prowloader/prow.go` around lines 361 - 374, The function
agePartitionsForDailyTestAnalysisByJob currently uses hard-coded retention
windows (90 for DetachOldPartitions and 100 for DropOldDetachedPartitions) which
control data deletion; make these retention values configurable instead of fixed
literals: add configurable fields or constants (e.g.,
ProwLoader.detachRetentionDays and ProwLoader.dropRetentionDays or read from
environment/config) and replace the literal 90 and 100 in the calls to
partitions.DetachOldPartitions and partitions.DropOldDetachedPartitions, ensure
the fields are initialized when ProwLoader is constructed (with sensible
defaults matching current behavior), and update the log messages to include the
configured retention values so behavior is clear per environment.
pkg/db/partitions/examples.go (1)

1-760: Avoid exporting Example helpers in production builds.

Because this file isn’t *_test.go, all Example... functions become part of the public package API and are compiled into binaries. If these are documentation-only, move them to an examples_test.go (or add a build tag) to prevent API bloat.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/partitions/examples.go` around lines 1 - 760, The file defines many
Example... functions (e.g., ExampleListPartitionedTables, ExampleListPartitions,
ExampleGetStats, ExampleComparePartitionStats, ExampleCheckRetentionPolicy,
ExampleDryRunCleanup, ExampleDetachedPartitions, ExampleAttachedPartitions,
ExampleDropOldDetachedPartitions, ExampleDetachWorkflow,
ExampleReattachPartition, ExampleCreateMissingPartitions,
ExampleCreatePartitionedTable, ExampleUpdatePartitionedTable,
ExampleWorkflowForAnyTable, ExampleCompleteWorkflow) that are exported in
production builds; move all these Example* helper functions out of the non-test
package file by either (A) relocating them into a new examples_test.go (or
examples_test.go in the same package) so they are only compiled for
tests/documentation, or (B) add an appropriate build constraint (e.g.,
//go:build examples) at the top of this file and adjust CI to avoid building
that tag by default—pick one approach and apply it consistently to all Example*
functions to prevent API bloat.
pkg/db/utils_example.go (1)

1-687: Avoid exporting Example helpers in production builds.

Since this isn’t a *_test.go, these Example... functions become exported API and ship in production binaries. If they’re documentation-only, consider moving to an examples_test.go (or using a build tag).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/utils_example.go` around lines 1 - 687, The Example* helper functions
(e.g., ExampleVerifyTablesHaveSameColumns, ExampleVerifyPartitionMatchesParent,
ExampleMigrateTableData, ExampleMigrateTableDataRange,
ExampleIncrementalMigrationByMonth, etc.) are defined in a non-test file and
will be included in production builds; move all Example... functions out of
pkg/db/utils_example.go into a new test file (e.g.,
pkg/db/utils_example_test.go) so they are only compiled for tests/documentation,
or alternatively add an appropriate build tag to exclude them from production.
To fix: create the new *_test.go file and copy every function named Example...
(referencing types/methods like DB.VerifyTablesHaveSameColumns,
DB.MigrateTableData, DB.MigrateTableDataRange, DB.SyncIdentityColumn,
DB.GetPartitionStrategy, DB.VerifyPartitionCoverage, etc.) into it (keeping
package db or db_test as desired), then remove the Example... definitions from
the production file so these helpers are not exported in production binaries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/dataloader/prowloader/prow.go`:
- Around line 369-372: Log messages incorrectly reference "detaching" for
operations that drop partitions and read stats; update the Errorf calls that
follow partitions.DropOldDetachedPartitions (where it currently logs "error
detaching partitions for %s") and the similar block around lines 385-387 to use
accurate text such as "error dropping partitions for %s" and "error reading
detached partition stats for %s" respectively, keeping the same
log.WithError(err).Errorf pattern and using tableName to format the message.

In `@pkg/db/partitions/partitions.go`:
- Around line 600-642: The DetachPartition function is vulnerable because it
interpolates tableName and partitionName directly into the DDL; change it to
quote/escape identifiers the same way used in DropPartition (i.e., wrap
identifiers with the safe quoting function used elsewhere) before building the
ALTER TABLE ... DETACH PARTITION SQL string. Locate DetachPartition and replace
direct usage of tableName and partitionName in the query construction with the
sanitized/quoted versions (use the project’s identifier quoting helper that
DropPartition uses), ensuring you still validate the name with
isValidPartitionName and preserve dryRun/logging behavior.
- Around line 976-999: The CREATE and ALTER DDL strings in createTableQuery and
attachQuery use unquoted identifiers (partitionName, tableName) and should use
the same identifier quoting helper used by DropPartition to avoid SQL injection
and reserved-word issues; update those format strings to quote/escape
identifiers (tableName and partitionName) before interpolation and likewise
quote range literals if your helper supports it. Also wrap the create + attach
sequence in a single DB transaction (begin, Exec CREATE, Exec ATTACH,
commit/rollback) so the cleanup DROP TABLE fallback is redundant; if you keep
cleanup, ensure the rollback path drops the quoted partitionName. Locate
createTableQuery, attachQuery, the Exec calls on dbc.DB and the cleanup Exec
call to apply these changes.
- Around line 1252-1260: The dynamically-built DDL uses raw table and column
identifiers (tableName, columns, partitionClause) and needs SQL identifier
quoting; add a small helper (e.g., quoteIdentifier(name string) string) that
wraps identifiers in the proper quote (") and escapes embedded quotes, then
apply it when building createTableSQL and any other DDL construction sites
(e.g., where columns are joined and where tableName is interpolated, and the
other locations noted around 1299-1301). Ensure you only quote the identifier
parts (preserve types/constraints in column definitions) and use the helper for
every occurrence instead of raw variables so all table/column names are
consistently quoted.
- Around line 1874-1882: The ALTER TABLE string interpolates unquoted
identifiers (tableName, partitionName); update the DDL to use properly quoted
identifiers by wrapping tableName and partitionName with the project's
identifier-quoting helper (e.g., QuoteIdentifier) or by adding a local
quoteIdentifier that escapes and double-quotes names (handling schema-qualified
names), then build query with fmt.Sprintf("ALTER TABLE %s ATTACH PARTITION %s
FOR VALUES FROM ('%s') TO ('%s')", QuoteIdentifier(tableName),
QuoteIdentifier(partitionName), startDate, endDate) so identifiers are
consistently quoted.
- Around line 1536-1540: The code currently unconditionally appends DROP COLUMN
statements for any entries in currentColMap, which can cause accidental data
loss; update the function that builds alterStatements (e.g.,
UpdatePartitionedTable) to accept a boolean flag like allowDropColumns (and
optionally dryRun) and only generate/append "ALTER TABLE ... DROP COLUMN ..."
when allowDropColumns is true and any required confirmation is provided; if
allowDropColumns is false, do not append DROP statements and instead log a WARN
via the same logger used elsewhere indicating which columns would be dropped
(listing keys from currentColMap) so the user can review; ensure unit tests and
call sites are updated to pass the new parameter and that logging is used
instead of silently adding destructive SQL.
- Around line 556-598: The DROP uses an unquoted identifier—wrap the partition
identifier with pq.QuoteIdentifier to prevent SQL injection even after
validation: in DropPartition (which calls extractTableNameFromPartition and
isValidPartitionName) replace query := fmt.Sprintf("DROP TABLE IF EXISTS %s",
partitionName) with building the query using pq.QuoteIdentifier(partitionName).
Also ensure the pq package is imported in this file (github.com/lib/pq) so the
QuoteIdentifier call compiles; keep all existing validation and logging
unchanged.

In `@pkg/db/partitions/README.md`:
- Around line 199-205: Update the README to correct the inconsistent retention
value: change the "Minimum retention period enforcement (30 days)" text in the
Input Validation section to "Minimum retention period enforcement (90 days)" so
it matches the Safety Checks heading and the actual code enforcement (see the
retentionDays < 90 check and the "Minimum 90 days retention" line).

---

Duplicate comments:
In `@pkg/db/utils.go`:
- Around line 686-771: GetTableRowCount and SyncIdentityColumn build SQL by
interpolating table/column identifiers directly; use the identifier-quoting
helper (e.g., QuoteIdentifier or quoteIdentifier) introduced in the earlier
comment to safely quote tableName and columnName before using fmt.Sprintf, and
keep values parameterized where possible (e.g., use dbc.DB.Raw("SELECT COUNT(*)
FROM "+QuoteIdentifier(tableName)).Scan(&count) and build the ALTER TABLE using
quoted identifiers: fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s RESTART WITH
%d", QuoteIdentifier(tableName), QuoteIdentifier(columnName), nextValue));
update references in GetTableRowCount and SyncIdentityColumn (including the
query/alterSQL variables and the dbc.DB.Raw/Exec calls) so identifiers are
quoted and SQL injection is prevented.

---

Nitpick comments:
In `@pkg/dataloader/prowloader/prow.go`:
- Around line 361-374: The function agePartitionsForDailyTestAnalysisByJob
currently uses hard-coded retention windows (90 for DetachOldPartitions and 100
for DropOldDetachedPartitions) which control data deletion; make these retention
values configurable instead of fixed literals: add configurable fields or
constants (e.g., ProwLoader.detachRetentionDays and ProwLoader.dropRetentionDays
or read from environment/config) and replace the literal 90 and 100 in the calls
to partitions.DetachOldPartitions and partitions.DropOldDetachedPartitions,
ensure the fields are initialized when ProwLoader is constructed (with sensible
defaults matching current behavior), and update the log messages to include the
configured retention values so behavior is clear per environment.

In `@pkg/db/partitions/examples.go`:
- Around line 1-760: The file defines many Example... functions (e.g.,
ExampleListPartitionedTables, ExampleListPartitions, ExampleGetStats,
ExampleComparePartitionStats, ExampleCheckRetentionPolicy, ExampleDryRunCleanup,
ExampleDetachedPartitions, ExampleAttachedPartitions,
ExampleDropOldDetachedPartitions, ExampleDetachWorkflow,
ExampleReattachPartition, ExampleCreateMissingPartitions,
ExampleCreatePartitionedTable, ExampleUpdatePartitionedTable,
ExampleWorkflowForAnyTable, ExampleCompleteWorkflow) that are exported in
production builds; move all these Example* helper functions out of the non-test
package file by either (A) relocating them into a new examples_test.go (or
examples_test.go in the same package) so they are only compiled for
tests/documentation, or (B) add an appropriate build constraint (e.g.,
//go:build examples) at the top of this file and adjust CI to avoid building
that tag by default—pick one approach and apply it consistently to all Example*
functions to prevent API bloat.

In `@pkg/db/utils_example.go`:
- Around line 1-687: The Example* helper functions (e.g.,
ExampleVerifyTablesHaveSameColumns, ExampleVerifyPartitionMatchesParent,
ExampleMigrateTableData, ExampleMigrateTableDataRange,
ExampleIncrementalMigrationByMonth, etc.) are defined in a non-test file and
will be included in production builds; move all Example... functions out of
pkg/db/utils_example.go into a new test file (e.g.,
pkg/db/utils_example_test.go) so they are only compiled for tests/documentation,
or alternatively add an appropriate build tag to exclude them from production.
To fix: create the new *_test.go file and copy every function named Example...
(referencing types/methods like DB.VerifyTablesHaveSameColumns,
DB.MigrateTableData, DB.MigrateTableDataRange, DB.SyncIdentityColumn,
DB.GetPartitionStrategy, DB.VerifyPartitionCoverage, etc.) into it (keeping
package db or db_test as desired), then remove the Example... definitions from
the production file so these helpers are not exported in production binaries.

Comment on lines +369 to +372
dropped, err := partitions.DropOldDetachedPartitions(pl.dbc, tableName, 100, false)
if err != nil {
log.WithError(err).Errorf("error detaching partitions for %s", tableName)
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix copy/paste log messages for drop/stats errors.

These logs currently mention “detaching” in contexts where we’re dropping or reading stats, which is misleading.

✏️ Suggested log text updates
-	log.WithError(err).Errorf("error detaching partitions for %s", tableName)
+	log.WithError(err).Errorf("error dropping detached partitions for %s", tableName)
-	log.WithError(err).Errorf("error detaching partitions for %s", tableName)
+	log.WithError(err).Errorf("error getting attached partition stats for %s", tableName)

Also applies to: 385-387

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/dataloader/prowloader/prow.go` around lines 369 - 372, Log messages
incorrectly reference "detaching" for operations that drop partitions and read
stats; update the Errorf calls that follow partitions.DropOldDetachedPartitions
(where it currently logs "error detaching partitions for %s") and the similar
block around lines 385-387 to use accurate text such as "error dropping
partitions for %s" and "error reading detached partition stats for %s"
respectively, keeping the same log.WithError(err).Errorf pattern and using
tableName to format the message.

Comment on lines +556 to +598
// DropPartition drops a single partition (DESTRUCTIVE - requires write access)
// This is a wrapper around DROP TABLE for safety and logging
func DropPartition(dbc *db.DB, partitionName string, dryRun bool) error {
start := time.Now()

// Extract table name from partition name
tableName, err := extractTableNameFromPartition(partitionName)
if err != nil {
return fmt.Errorf("invalid partition name: %w", err)
}

// Validate partition name format for safety
if !isValidPartitionName(tableName, partitionName) {
return fmt.Errorf("invalid partition name: %s - must match %s_YYYY_MM_DD", partitionName, tableName)
}

if dryRun {
log.WithFields(log.Fields{
"partition": partitionName,
"table": tableName,
}).Info("[DRY RUN] would drop partition")
return nil
}

query := fmt.Sprintf("DROP TABLE IF EXISTS %s", partitionName)
result := dbc.DB.Exec(query)
if result.Error != nil {
log.WithError(result.Error).WithFields(log.Fields{
"partition": partitionName,
"table": tableName,
}).Error("failed to drop partition")
return result.Error
}

elapsed := time.Since(start)
log.WithFields(log.Fields{
"partition": partitionName,
"table": tableName,
"elapsed": elapsed,
}).Info("dropped partition")

return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for identifier quoting utility in codebase
rg -n "quote.*ident|QuoteIdent" --type=go

Repository: openshift/sippy

Length of output: 1958


🏁 Script executed:

#!/bin/bash
# Check if pq library is available
rg -n '"github.com/lib/pq"' --type=go

Repository: openshift/sippy

Length of output: 916


🏁 Script executed:

#!/bin/bash
# Find and examine isValidPartitionName function
rg -n "func isValidPartitionName" --type=go -A 10

Repository: openshift/sippy

Length of output: 766


🏁 Script executed:

#!/bin/bash
# Find and examine extractTableNameFromPartition function
rg -n "func extractTableNameFromPartition" --type=go -A 10

Repository: openshift/sippy

Length of output: 903


Use pq.QuoteIdentifier for identifier quoting.

While isValidPartitionName provides robust validation (exact length check, prefix validation, and date format parsing via time.Parse), identifier quoting should still be used as a defense-in-depth best practice. The github.com/lib/pq library is already imported elsewhere in the project (e.g., pkg/filter/filterable.go).

Recommended fix
-	query := fmt.Sprintf("DROP TABLE IF EXISTS %s", partitionName)
+	query := "DROP TABLE IF EXISTS " + pq.QuoteIdentifier(partitionName)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/partitions/partitions.go` around lines 556 - 598, The DROP uses an
unquoted identifier—wrap the partition identifier with pq.QuoteIdentifier to
prevent SQL injection even after validation: in DropPartition (which calls
extractTableNameFromPartition and isValidPartitionName) replace query :=
fmt.Sprintf("DROP TABLE IF EXISTS %s", partitionName) with building the query
using pq.QuoteIdentifier(partitionName). Also ensure the pq package is imported
in this file (github.com/lib/pq) so the QuoteIdentifier call compiles; keep all
existing validation and logging unchanged.

Comment on lines +600 to +642
// DetachPartition detaches a partition from the parent table (safer alternative to DROP)
// The detached table can be archived or dropped later
func DetachPartition(dbc *db.DB, partitionName string, dryRun bool) error {
start := time.Now()

// Extract table name from partition name
tableName, err := extractTableNameFromPartition(partitionName)
if err != nil {
return fmt.Errorf("invalid partition name: %w", err)
}

// Validate partition name format for safety
if !isValidPartitionName(tableName, partitionName) {
return fmt.Errorf("invalid partition name: %s - must match %s_YYYY_MM_DD", partitionName, tableName)
}

if dryRun {
log.WithFields(log.Fields{
"partition": partitionName,
"table": tableName,
}).Info("[DRY RUN] would detach partition")
return nil
}

query := fmt.Sprintf("ALTER TABLE %s DETACH PARTITION %s", tableName, partitionName)
result := dbc.DB.Exec(query)
if result.Error != nil {
log.WithError(result.Error).WithFields(log.Fields{
"partition": partitionName,
"table": tableName,
}).Error("failed to detach partition")
return result.Error
}

elapsed := time.Since(start)
log.WithFields(log.Fields{
"partition": partitionName,
"table": tableName,
"elapsed": elapsed,
}).Info("detached partition")

return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same identifier quoting concern as DropPartition.

The tableName and partitionName are interpolated directly into the DDL statement at Line 624. Apply the same quoting approach recommended above.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/partitions/partitions.go` around lines 600 - 642, The DetachPartition
function is vulnerable because it interpolates tableName and partitionName
directly into the DDL; change it to quote/escape identifiers the same way used
in DropPartition (i.e., wrap identifiers with the safe quoting function used
elsewhere) before building the ALTER TABLE ... DETACH PARTITION SQL string.
Locate DetachPartition and replace direct usage of tableName and partitionName
in the query construction with the sanitized/quoted versions (use the project’s
identifier quoting helper that DropPartition uses), ensuring you still validate
the name with isValidPartitionName and preserve dryRun/logging behavior.

Comment on lines +976 to +999

// Create the partition table with same structure as parent
createTableQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (LIKE %s INCLUDING ALL)", partitionName, tableName)
result := dbc.DB.Exec(createTableQuery)
if result.Error != nil {
log.WithError(result.Error).WithField("partition", partitionName).Error("failed to create partition table")
continue
}

// Attach the partition to the parent table
attachQuery := fmt.Sprintf(
"ALTER TABLE %s ATTACH PARTITION %s FOR VALUES FROM ('%s') TO ('%s')",
tableName,
partitionName,
rangeStart,
rangeEnd,
)
result = dbc.DB.Exec(attachQuery)
if result.Error != nil {
// If attach fails, try to clean up the created table
log.WithError(result.Error).WithField("partition", partitionName).Error("failed to attach partition")
dbc.DB.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", partitionName))
continue
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Identifier quoting needed in DDL statements.

Lines 978, 986-992, and 997 construct DDL statements with unquoted identifiers. Apply the same identifier quoting approach as recommended for DropPartition.

Additionally, the cleanup logic at Line 997 is good defensive coding, but consider wrapping the create+attach in a transaction for atomicity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/partitions/partitions.go` around lines 976 - 999, The CREATE and ALTER
DDL strings in createTableQuery and attachQuery use unquoted identifiers
(partitionName, tableName) and should use the same identifier quoting helper
used by DropPartition to avoid SQL injection and reserved-word issues; update
those format strings to quote/escape identifiers (tableName and partitionName)
before interpolation and likewise quote range literals if your helper supports
it. Also wrap the create + attach sequence in a single DB transaction (begin,
Exec CREATE, Exec ATTACH, commit/rollback) so the cleanup DROP TABLE fallback is
redundant; if you keep cleanup, ensure the rollback path drops the quoted
partitionName. Locate createTableQuery, attachQuery, the Exec calls on dbc.DB
and the cleanup Exec call to apply these changes.

Comment on lines +1252 to +1260
// Build the CREATE TABLE statement with partition strategy
partitionClause := config.ToSQL()
createTableSQL := fmt.Sprintf(
"CREATE TABLE IF NOT EXISTS %s (\n %s\n) %s",
tableName,
strings.Join(columns, ",\n "),
partitionClause,
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Apply identifier quoting to dynamically constructed DDL.

The table name and column names at Lines 1255, 1299-1301, and elsewhere should be quoted. While table/column names from GORM models are typically safe, applying consistent quoting is a defensive best practice.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/partitions/partitions.go` around lines 1252 - 1260, The
dynamically-built DDL uses raw table and column identifiers (tableName, columns,
partitionClause) and needs SQL identifier quoting; add a small helper (e.g.,
quoteIdentifier(name string) string) that wraps identifiers in the proper quote
(") and escapes embedded quotes, then apply it when building createTableSQL and
any other DDL construction sites (e.g., where columns are joined and where
tableName is interpolated, and the other locations noted around 1299-1301).
Ensure you only quote the identifier parts (preserve types/constraints in column
definitions) and use the helper for every occurrence instead of raw variables so
all table/column names are consistently quoted.

Comment on lines +1536 to +1540
// Remaining columns in map should be dropped
for colName := range currentColMap {
alterStatements = append(alterStatements,
fmt.Sprintf("ALTER TABLE %s DROP COLUMN %s", tableName, colName))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Dropping columns without confirmation could be dangerous.

Line 1537-1539 automatically generates DROP COLUMN statements for columns present in the database but not in the model. This could unintentionally cause data loss if a model field is accidentally removed or renamed.

Consider:

  1. Adding a separate flag to enable column drops (disabled by default)
  2. Requiring explicit confirmation for destructive operations
  3. At minimum, logging at WARN level instead of just including in alterStatements
🛡️ Proposed safer approach
 	// Remaining columns in map should be dropped
+	// WARNING: Only drop columns if explicitly enabled to prevent accidental data loss
 	for colName := range currentColMap {
-		alterStatements = append(alterStatements,
-			fmt.Sprintf("ALTER TABLE %s DROP COLUMN %s", tableName, colName))
+		log.WithFields(log.Fields{
+			"table":  tableName,
+			"column": colName,
+		}).Warn("column exists in database but not in model - consider manual removal if intended")
 	}

Or add a parameter to control this behavior:

func UpdatePartitionedTable(dbc *db.DB, model interface{}, tableName string, allowDropColumns bool, dryRun bool) (string, error) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/partitions/partitions.go` around lines 1536 - 1540, The code currently
unconditionally appends DROP COLUMN statements for any entries in currentColMap,
which can cause accidental data loss; update the function that builds
alterStatements (e.g., UpdatePartitionedTable) to accept a boolean flag like
allowDropColumns (and optionally dryRun) and only generate/append "ALTER TABLE
... DROP COLUMN ..." when allowDropColumns is true and any required confirmation
is provided; if allowDropColumns is false, do not append DROP statements and
instead log a WARN via the same logger used elsewhere indicating which columns
would be dropped (listing keys from currentColMap) so the user can review;
ensure unit tests and call sites are updated to pass the new parameter and that
logging is used instead of silently adding destructive SQL.

Comment on lines +1874 to +1882

// Reattach the partition with FOR VALUES clause
query := fmt.Sprintf(
"ALTER TABLE %s ATTACH PARTITION %s FOR VALUES FROM ('%s') TO ('%s')",
tableName,
partitionName,
startDate,
endDate,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Apply identifier quoting for DDL statement.

Line 1876-1882 constructs the ALTER TABLE ATTACH PARTITION statement with unquoted identifiers. Apply consistent identifier quoting as recommended earlier.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/partitions/partitions.go` around lines 1874 - 1882, The ALTER TABLE
string interpolates unquoted identifiers (tableName, partitionName); update the
DDL to use properly quoted identifiers by wrapping tableName and partitionName
with the project's identifier-quoting helper (e.g., QuoteIdentifier) or by
adding a local quoteIdentifier that escapes and double-quotes names (handling
schema-qualified names), then build query with fmt.Sprintf("ALTER TABLE %s
ATTACH PARTITION %s FOR VALUES FROM ('%s') TO ('%s')",
QuoteIdentifier(tableName), QuoteIdentifier(partitionName), startDate, endDate)
so identifiers are consistently quoted.

Comment on lines +199 to +205
**Safety Checks**:
- Minimum 90 days retention
- Maximum 75% of attached partitions deleted
- Maximum 80% of attached storage deleted

**Important**: Only considers **attached partitions** when validating thresholds. Detached partitions are excluded from calculations to ensure the policy is safe for active data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's locate the README file in the partitions directory
fd -t f README.md -p "*/partitions/*"

Repository: openshift/sippy

Length of output: 160


🏁 Script executed:

# Read the README file around the cited lines
wc -l pkg/db/partitions/README.md

Repository: openshift/sippy

Length of output: 91


🏁 Script executed:

# Check the specific lines mentioned in the review
sed -n '195,210p' pkg/db/partitions/README.md

Repository: openshift/sippy

Length of output: 583


🏁 Script executed:

# Check lines 1442-1445
sed -n '1438,1450p' pkg/db/partitions/README.md

Repository: openshift/sippy

Length of output: 412


🏁 Script executed:

# Search for retention period enforcement in the partitions package
rg -A 3 -B 3 "retention|RetentionDays" pkg/db/partitions/ --type-list | head -30

Repository: openshift/sippy

Length of output: 448


🏁 Script executed:

# Look for any constants or validation that enforces minimum retention
rg "30|90" pkg/db/partitions/ -A 2 -B 2 --type go

Repository: openshift/sippy

Length of output: 1413


🏁 Script executed:

# Search more broadly for retention validation logic
rg "Minimum.*retention|retention.*minimum" pkg/db/partitions/ -i

Repository: openshift/sippy

Length of output: 392


Update documentation to correct retention period inconsistency.

The README states "Minimum 90 days retention" in the Safety Checks section, but "Minimum retention period enforcement (30 days)" in the Input Validation section. The actual code enforces a 90-day minimum (if retentionDays < 90), so the 30-day value must be corrected to 90 days to align with both the stated policy and implementation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/db/partitions/README.md` around lines 199 - 205, Update the README to
correct the inconsistent retention value: change the "Minimum retention period
enforcement (30 days)" text in the Input Validation section to "Minimum
retention period enforcement (90 days)" so it matches the Safety Checks heading
and the actual code enforcement (see the retentionDays < 90 check and the
"Minimum 90 days retention" line).

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 18, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: neisw

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants