mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-11-07 13:13:05 -05:00
Coords holds a space-separated list of "x,y" points, written with two
decimal places by pointsToCoords()/mapCoords(). Since 1.39.2 the Zones
values are percentages rather than pixels, which costs up to 13 bytes per
point ("100.00,100.00") against the 9 a pixel pair used ("1920,1080").
tinytext caps at 255 bytes, so a zone of roughly 19 points or more
overflows and is silently truncated, leaving a corrupt polygon. In pixel
form the same zone fit, so this only starts biting at the conversion.
Widen the column in 1.39.2 before its conversion runs, and again in a new
1.39.23 for installations that ran that conversion before the widening
existed - zmupdate.pl only applies files at or above the database version,
so those never revisit 1.39.2. ALTER ... MODIFY to the same type is a
no-op, so both are safe to re-run.
Maps stores coordinates in the same format and is widened to match. It
was added to zm_create.sql.in in 1.32 without a matching update script,
so installations older than that have never had the table; guard that
statement on the table existing rather than failing the whole migration.
Update zm_create.sql.in for both tables so a fresh install matches an
upgraded one, and bump version.txt so the new update script is reached.
150 lines
5.1 KiB
SQL
150 lines
5.1 KiB
SQL
--
|
|
-- Convert Zone Coords from pixel values to percentage values (0.00-100.00)
|
|
-- so that zones are resolution-independent.
|
|
--
|
|
|
|
-- Coords is tinytext (255 bytes). The percentage form is longer than the pixel
|
|
-- form it replaces -- up to 13 bytes per point ("100.00,100.00") against 9
|
|
-- ("1920,1080") -- so a zone with enough points can overflow and be silently
|
|
-- truncated by the conversion below, which destroys the polygon. Widen the
|
|
-- column first. See also zm_update-1.39.23.sql, which does the same for
|
|
-- installations that are already past this version.
|
|
ALTER TABLE Zones MODIFY `Coords` TEXT NOT NULL;
|
|
|
|
DELIMITER //
|
|
|
|
DROP PROCEDURE IF EXISTS `zm_update_zone_coords_to_percent` //
|
|
|
|
CREATE PROCEDURE `zm_update_zone_coords_to_percent`()
|
|
BEGIN
|
|
DECLARE done INT DEFAULT FALSE;
|
|
DECLARE v_zone_id INT;
|
|
DECLARE v_coords TINYTEXT;
|
|
DECLARE v_mon_width INT;
|
|
DECLARE v_mon_height INT;
|
|
DECLARE v_new_coords TEXT DEFAULT '';
|
|
DECLARE v_pair TEXT;
|
|
DECLARE v_x_str TEXT;
|
|
DECLARE v_y_str TEXT;
|
|
DECLARE v_x_pct TEXT;
|
|
DECLARE v_y_pct TEXT;
|
|
DECLARE v_remaining TEXT;
|
|
DECLARE v_space_pos INT;
|
|
|
|
DECLARE cur CURSOR FOR
|
|
SELECT z.Id, z.Coords, m.Width, m.Height
|
|
FROM Zones z
|
|
JOIN Monitors m ON z.MonitorId = m.Id
|
|
WHERE m.Width > 0 AND m.Height > 0;
|
|
|
|
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
|
|
|
|
OPEN cur;
|
|
|
|
read_loop: LOOP
|
|
FETCH cur INTO v_zone_id, v_coords, v_mon_width, v_mon_height;
|
|
IF done THEN
|
|
LEAVE read_loop;
|
|
END IF;
|
|
|
|
-- Skip if coords already look like percentages (contain a decimal point)
|
|
IF v_coords LIKE '%.%' THEN
|
|
ITERATE read_loop;
|
|
END IF;
|
|
|
|
SET v_new_coords = '';
|
|
SET v_remaining = TRIM(v_coords);
|
|
|
|
-- Parse each space-separated x,y pair
|
|
coord_loop: LOOP
|
|
IF v_remaining = '' OR v_remaining IS NULL THEN
|
|
LEAVE coord_loop;
|
|
END IF;
|
|
|
|
SET v_space_pos = LOCATE(' ', v_remaining);
|
|
IF v_space_pos > 0 THEN
|
|
SET v_pair = LEFT(v_remaining, v_space_pos - 1);
|
|
SET v_remaining = TRIM(SUBSTRING(v_remaining, v_space_pos + 1));
|
|
ELSE
|
|
SET v_pair = v_remaining;
|
|
SET v_remaining = '';
|
|
END IF;
|
|
|
|
-- Skip empty pairs (from double spaces, trailing commas etc)
|
|
IF v_pair = '' OR v_pair = ',' THEN
|
|
ITERATE coord_loop;
|
|
END IF;
|
|
|
|
-- Split on comma
|
|
SET v_x_str = SUBSTRING_INDEX(v_pair, ',', 1);
|
|
SET v_y_str = SUBSTRING_INDEX(v_pair, ',', -1);
|
|
|
|
-- Convert to percentage with 2 decimal places
|
|
-- Use CAST to DECIMAL which always uses '.' as decimal separator (locale-independent)
|
|
SET v_x_pct = CAST(ROUND(CAST(v_x_str AS DECIMAL(10,2)) / v_mon_width * 100, 2) AS DECIMAL(10,2));
|
|
SET v_y_pct = CAST(ROUND(CAST(v_y_str AS DECIMAL(10,2)) / v_mon_height * 100, 2) AS DECIMAL(10,2));
|
|
|
|
IF v_new_coords != '' THEN
|
|
SET v_new_coords = CONCAT(v_new_coords, ' ');
|
|
END IF;
|
|
SET v_new_coords = CONCAT(v_new_coords, v_x_pct, ',', v_y_pct);
|
|
|
|
END LOOP coord_loop;
|
|
|
|
IF v_new_coords != '' THEN
|
|
-- Rescale Area in the same statement so it can only ever be applied to a
|
|
-- zone whose Coords were actually just converted. A zone that was skipped
|
|
-- above (already percent) keeps its Area untouched, which is what makes
|
|
-- re-running this file safe.
|
|
UPDATE Zones
|
|
SET Coords = v_new_coords,
|
|
Area = IF(Area > 0, ROUND(Area * 10000.0 / (v_mon_width * v_mon_height)), Area)
|
|
WHERE Id = v_zone_id;
|
|
END IF;
|
|
|
|
END LOOP read_loop;
|
|
|
|
CLOSE cur;
|
|
END //
|
|
|
|
DELIMITER ;
|
|
|
|
CALL zm_update_zone_coords_to_percent();
|
|
DROP PROCEDURE IF EXISTS `zm_update_zone_coords_to_percent`;
|
|
|
|
-- Update Units to Percent for all zones, and set as new default
|
|
UPDATE Zones SET Units = 'Percent' WHERE Units = 'Pixels';
|
|
ALTER TABLE Zones ALTER Units SET DEFAULT 'Percent';
|
|
|
|
--
|
|
-- Add Notifications table for FCM push token registration
|
|
--
|
|
|
|
SET @s = (SELECT IF(
|
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 'Notifications'
|
|
AND table_schema = DATABASE()) > 0,
|
|
"SELECT 'Notifications table already exists'",
|
|
"CREATE TABLE `Notifications` (
|
|
`Id` int unsigned NOT NULL AUTO_INCREMENT,
|
|
`UserId` int unsigned DEFAULT NULL,
|
|
`Token` varchar(512) NOT NULL,
|
|
`Platform` enum('android','ios','web') NOT NULL,
|
|
`MonitorList` text DEFAULT NULL,
|
|
`Interval` int unsigned NOT NULL DEFAULT 0,
|
|
`PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled',
|
|
`AppVersion` varchar(32) DEFAULT NULL,
|
|
`BadgeCount` int NOT NULL DEFAULT 0,
|
|
`LastNotifiedAt` datetime DEFAULT NULL,
|
|
`CreatedOn` datetime DEFAULT NULL,
|
|
`UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`Id`),
|
|
UNIQUE KEY `Notifications_Token_idx` (`Token`),
|
|
KEY `Notifications_UserId_idx` (`UserId`),
|
|
CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE
|
|
) ENGINE=InnoDB"
|
|
));
|
|
|
|
PREPARE stmt FROM @s;
|
|
EXECUTE stmt;
|
|
DEALLOCATE PREPARE stmt;
|