Added missing FK constraint on scheduled_queries

This commit is contained in:
Juan Fernandez
2023-07-10 14:53:00 -04:00
parent 2b8dd65716
commit 1151177938
4 changed files with 40 additions and 25 deletions
@@ -11,11 +11,12 @@ func init() {
}
func Up_20230706141219(tx *sql.Tx) error {
// If we want to drop the uniq constraint on queries.name, we first need to remove this
// constraint on scheduled_queries, due to a FK constraint scheduled_queries (query_name) =>
// queries (name).
// Drop FK constraint based on queries (name) - since the uniqueness constraint on the queries
// table changed.
if _, err := tx.Exec(`
ALTER TABLE scheduled_queries DROP FOREIGN KEY scheduled_queries_query_name;
ALTER TABLE scheduled_queries
ADD team_id_char CHAR(10) DEFAULT '',
DROP FOREIGN KEY scheduled_queries_query_name;
`); err != nil {
return errors.Wrap(err, "removing FK on scheduled_queries")
}
@@ -26,7 +27,7 @@ func Up_20230706141219(tx *sql.Tx) error {
DROP INDEX constraint_query_name_unique,
ADD team_id INT(10) UNSIGNED DEFAULT NULL,
ADD team_id_char CHAR(10) DEFAULT '',
ADD team_id_char CHAR(10) DEFAULT '' NOT NULL,
ADD platform VARCHAR(255) DEFAULT '' NOT NULL,
ADD min_osquery_version VARCHAR(255) DEFAULT '' NOT NULL,
@@ -35,12 +36,20 @@ func Up_20230706141219(tx *sql.Tx) error {
ADD automations_enabled TINYINT(1) UNSIGNED DEFAULT 0 NOT NULL,
ADD logging_type VARCHAR(255) DEFAULT 'snapshot' NOT NULL,
ADD FOREIGN KEY fk_queries_team_id (team_id) REFERENCES teams (id) ON DELETE CASCADE ON UPDATE CASCADE,
ADD FOREIGN KEY fk_queries_team_id (team_id) REFERENCES teams (id) ON DELETE CASCADE,
ADD UNIQUE INDEX idx_team_id_name_unq (team_id_char, name);
`); err != nil {
return errors.Wrap(err, "updating queries schema")
}
// Add new FK constraint to make sure all scheduled_queries exists as 'global' queries.
if _, err := tx.Exec(`
ALTER TABLE scheduled_queries
ADD FOREIGN KEY fk_scheduled_queries_queries (team_id_char, query_name) REFERENCES queries (team_id_char, name);
`); err != nil {
return errors.Wrap(err, "adding new FK on scheduled_queries")
}
return nil
}
+14
View File
@@ -1041,6 +1041,20 @@ func generateMysqlConnectionString(conf config.MysqlConfig) string {
return dsn
}
// isForeignKeyError checks if the provided error is a MySQL child foreign key
// error (Error #1452)
func isChildForeignKeyError(err error) bool {
err = ctxerr.Cause(err)
mysqlErr, ok := err.(*mysql.MySQLError)
if !ok {
return false
}
// https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_no_referenced_row_2
const ER_NO_REFERENCED_ROW_2 = 1452
return mysqlErr.Number == ER_NO_REFERENCED_ROW_2
}
type patternReplacer func(string) string
// likePattern returns a pattern to match m with LIKE.
+6 -17
View File
@@ -75,22 +75,7 @@ func applyPackSpecDB(ctx context.Context, tx sqlx.ExtContext, spec *fleet.PackSp
if q.Name == "" {
q.Name = q.QueryName
}
// Check if query exists ... we have to do this manually because the FK
// constraint was removed as part of the work required for combining queries and schedules
var count int
if err := tx.QueryRowxContext(
ctx,
`SELECT COUNT(1) FROM queries WHERE team_id_char = '' AND name = ?`,
q.QueryName,
).Scan(&count); err != nil {
return ctxerr.Wrap(ctx, err, "checking if query exists")
}
if count == 0 {
return ctxerr.Errorf(ctx, "cannot schedule unknown query '%s'", q.QueryName)
}
if _, err := tx.ExecContext(ctx, query,
_, err := tx.ExecContext(ctx, query,
packID,
q.QueryName,
q.Name,
@@ -102,7 +87,11 @@ func applyPackSpecDB(ctx context.Context, tx sqlx.ExtContext, spec *fleet.PackSp
q.Platform,
q.Version,
q.Denylist,
); err != nil {
)
switch {
case isChildForeignKeyError(err):
return ctxerr.Errorf(ctx, "cannot schedule unknown query '%s'", q.QueryName)
case err != nil:
return ctxerr.Wrapf(ctx, err, "adding query %s referencing %s", q.Name, q.QueryName)
}
}
+5 -2
View File
@@ -1018,7 +1018,7 @@ CREATE TABLE `queries` (
`author_id` int(10) unsigned DEFAULT NULL,
`observer_can_run` tinyint(1) NOT NULL DEFAULT '0',
`team_id` int(10) unsigned DEFAULT NULL,
`team_id_char` char(10) COLLATE utf8mb4_unicode_ci DEFAULT '',
`team_id_char` char(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`platform` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`min_osquery_version` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`schedule_interval` int(10) unsigned NOT NULL DEFAULT '0',
@@ -1029,7 +1029,7 @@ CREATE TABLE `queries` (
KEY `author_id` (`author_id`),
KEY `fk_queries_team_id` (`team_id`),
CONSTRAINT `queries_ibfk_1` FOREIGN KEY (`author_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `queries_ibfk_2` FOREIGN KEY (`team_id`) REFERENCES `teams` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
CONSTRAINT `queries_ibfk_2` FOREIGN KEY (`team_id`) REFERENCES `teams` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40101 SET @saved_cs_client = @@character_set_client */;
@@ -1073,10 +1073,13 @@ CREATE TABLE `scheduled_queries` (
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` varchar(1023) COLLATE utf8mb4_unicode_ci DEFAULT '',
`denylist` tinyint(1) DEFAULT NULL,
`team_id_char` char(10) COLLATE utf8mb4_unicode_ci DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_names_in_packs` (`name`,`pack_id`),
KEY `scheduled_queries_pack_id` (`pack_id`),
KEY `scheduled_queries_query_name` (`query_name`),
KEY `fk_scheduled_queries_queries` (`team_id_char`,`query_name`),
CONSTRAINT `scheduled_queries_ibfk_1` FOREIGN KEY (`team_id_char`, `query_name`) REFERENCES `queries` (`team_id_char`, `name`),
CONSTRAINT `scheduled_queries_pack_id` FOREIGN KEY (`pack_id`) REFERENCES `packs` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;