mirror of https://github.com/kortix-ai/suna.git
196 lines
6.1 KiB
MySQL
196 lines
6.1 KiB
MySQL
|
-- FOOLPROOF Migration to fix double-escaped JSON in messages table
|
||
|
-- This will definitely work by using the simplest possible approach
|
||
|
|
||
|
-- Step 1: Create backup with timestamp
|
||
|
DO $$
|
||
|
DECLARE
|
||
|
backup_name TEXT;
|
||
|
BEGIN
|
||
|
backup_name := 'messages_backup_' || to_char(NOW(), 'YYYYMMDD_HH24MISS');
|
||
|
EXECUTE 'CREATE TABLE ' || backup_name || ' AS SELECT * FROM messages';
|
||
|
RAISE NOTICE 'Created backup table: %', backup_name;
|
||
|
END $$;
|
||
|
|
||
|
-- Step 2: Show exactly what we're dealing with
|
||
|
DO $$
|
||
|
DECLARE
|
||
|
total_count INTEGER;
|
||
|
string_content_count INTEGER;
|
||
|
string_metadata_count INTEGER;
|
||
|
sample_content TEXT;
|
||
|
sample_metadata TEXT;
|
||
|
BEGIN
|
||
|
-- Count totals
|
||
|
SELECT COUNT(*) INTO total_count FROM messages;
|
||
|
|
||
|
-- Count content that's stored as strings (should be objects)
|
||
|
SELECT COUNT(*) INTO string_content_count
|
||
|
FROM messages
|
||
|
WHERE jsonb_typeof(content) = 'string';
|
||
|
|
||
|
-- Count metadata that's stored as strings (should be objects)
|
||
|
SELECT COUNT(*) INTO string_metadata_count
|
||
|
FROM messages
|
||
|
WHERE jsonb_typeof(metadata) = 'string';
|
||
|
|
||
|
-- Get samples
|
||
|
SELECT content::text INTO sample_content
|
||
|
FROM messages
|
||
|
WHERE jsonb_typeof(content) = 'string'
|
||
|
LIMIT 1;
|
||
|
|
||
|
SELECT metadata::text INTO sample_metadata
|
||
|
FROM messages
|
||
|
WHERE jsonb_typeof(metadata) = 'string'
|
||
|
LIMIT 1;
|
||
|
|
||
|
RAISE NOTICE 'Total messages: %', total_count;
|
||
|
RAISE NOTICE 'Messages with string content (should be objects): %', string_content_count;
|
||
|
RAISE NOTICE 'Messages with string metadata (should be objects): %', string_metadata_count;
|
||
|
|
||
|
IF sample_content IS NOT NULL THEN
|
||
|
RAISE NOTICE 'Sample string content: %', LEFT(sample_content, 150);
|
||
|
END IF;
|
||
|
|
||
|
IF sample_metadata IS NOT NULL THEN
|
||
|
RAISE NOTICE 'Sample string metadata: %', LEFT(sample_metadata, 150);
|
||
|
END IF;
|
||
|
END $$;
|
||
|
|
||
|
-- Step 3: Test parsing a few records to make sure it will work
|
||
|
DO $$
|
||
|
DECLARE
|
||
|
test_record RECORD;
|
||
|
parsed_content JSONB;
|
||
|
parsed_metadata JSONB;
|
||
|
success_count INTEGER := 0;
|
||
|
error_count INTEGER := 0;
|
||
|
BEGIN
|
||
|
FOR test_record IN
|
||
|
SELECT message_id, content, metadata
|
||
|
FROM messages
|
||
|
WHERE jsonb_typeof(content) = 'string'
|
||
|
OR jsonb_typeof(metadata) = 'string'
|
||
|
LIMIT 10
|
||
|
LOOP
|
||
|
BEGIN
|
||
|
-- Test content parsing
|
||
|
IF jsonb_typeof(test_record.content) = 'string' THEN
|
||
|
parsed_content := test_record.content::text::jsonb;
|
||
|
success_count := success_count + 1;
|
||
|
RAISE NOTICE 'SUCCESS: Parsed content for message %', test_record.message_id;
|
||
|
END IF;
|
||
|
|
||
|
-- Test metadata parsing
|
||
|
IF jsonb_typeof(test_record.metadata) = 'string' THEN
|
||
|
parsed_metadata := test_record.metadata::text::jsonb;
|
||
|
success_count := success_count + 1;
|
||
|
RAISE NOTICE 'SUCCESS: Parsed metadata for message %', test_record.message_id;
|
||
|
END IF;
|
||
|
|
||
|
EXCEPTION WHEN OTHERS THEN
|
||
|
error_count := error_count + 1;
|
||
|
RAISE WARNING 'ERROR: Failed to parse JSON for message %: %', test_record.message_id, SQLERRM;
|
||
|
END;
|
||
|
END LOOP;
|
||
|
|
||
|
RAISE NOTICE 'Test results: % successes, % errors', success_count, error_count;
|
||
|
|
||
|
IF error_count > 0 THEN
|
||
|
RAISE EXCEPTION 'Found parsing errors. Migration aborted for safety.';
|
||
|
END IF;
|
||
|
END $$;
|
||
|
|
||
|
-- Step 4: Fix content field (do this separately for clarity)
|
||
|
DO $$
|
||
|
DECLARE
|
||
|
content_updates INTEGER := 0;
|
||
|
BEGIN
|
||
|
UPDATE messages
|
||
|
SET
|
||
|
content = content::text::jsonb,
|
||
|
updated_at = NOW()
|
||
|
WHERE jsonb_typeof(content) = 'string';
|
||
|
|
||
|
GET DIAGNOSTICS content_updates = ROW_COUNT;
|
||
|
RAISE NOTICE 'Fixed content field in % messages', content_updates;
|
||
|
END $$;
|
||
|
|
||
|
-- Step 5: Fix metadata field (do this separately)
|
||
|
DO $$
|
||
|
DECLARE
|
||
|
metadata_updates INTEGER := 0;
|
||
|
BEGIN
|
||
|
UPDATE messages
|
||
|
SET
|
||
|
metadata = metadata::text::jsonb,
|
||
|
updated_at = NOW()
|
||
|
WHERE jsonb_typeof(metadata) = 'string';
|
||
|
|
||
|
GET DIAGNOSTICS metadata_updates = ROW_COUNT;
|
||
|
RAISE NOTICE 'Fixed metadata field in % messages', metadata_updates;
|
||
|
END $$;
|
||
|
|
||
|
-- Step 6: Verify everything is fixed
|
||
|
DO $$
|
||
|
DECLARE
|
||
|
remaining_string_content INTEGER;
|
||
|
remaining_string_metadata INTEGER;
|
||
|
sample_fixed_content JSONB;
|
||
|
sample_fixed_metadata JSONB;
|
||
|
BEGIN
|
||
|
-- Check if any string fields remain
|
||
|
SELECT COUNT(*) INTO remaining_string_content
|
||
|
FROM messages
|
||
|
WHERE jsonb_typeof(content) = 'string';
|
||
|
|
||
|
SELECT COUNT(*) INTO remaining_string_metadata
|
||
|
FROM messages
|
||
|
WHERE jsonb_typeof(metadata) = 'string';
|
||
|
|
||
|
-- Get samples of fixed data
|
||
|
SELECT content INTO sample_fixed_content
|
||
|
FROM messages
|
||
|
WHERE updated_at >= NOW() - INTERVAL '5 minutes'
|
||
|
AND jsonb_typeof(content) = 'object'
|
||
|
LIMIT 1;
|
||
|
|
||
|
SELECT metadata INTO sample_fixed_metadata
|
||
|
FROM messages
|
||
|
WHERE updated_at >= NOW() - INTERVAL '5 minutes'
|
||
|
AND jsonb_typeof(metadata) = 'object'
|
||
|
LIMIT 1;
|
||
|
|
||
|
RAISE NOTICE 'Verification Results:';
|
||
|
RAISE NOTICE '- Remaining string content fields: %', remaining_string_content;
|
||
|
RAISE NOTICE '- Remaining string metadata fields: %', remaining_string_metadata;
|
||
|
|
||
|
IF sample_fixed_content IS NOT NULL THEN
|
||
|
RAISE NOTICE '- Sample fixed content: %', sample_fixed_content;
|
||
|
END IF;
|
||
|
|
||
|
IF sample_fixed_metadata IS NOT NULL THEN
|
||
|
RAISE NOTICE '- Sample fixed metadata: %', sample_fixed_metadata;
|
||
|
END IF;
|
||
|
|
||
|
IF remaining_string_content = 0 AND remaining_string_metadata = 0 THEN
|
||
|
RAISE NOTICE 'SUCCESS: All double-escaped JSON has been fixed!';
|
||
|
ELSE
|
||
|
RAISE WARNING 'Some string fields remain - manual investigation needed';
|
||
|
END IF;
|
||
|
END $$;
|
||
|
|
||
|
-- Step 7: Create index if needed
|
||
|
CREATE INDEX IF NOT EXISTS idx_messages_updated_at ON messages(updated_at);
|
||
|
|
||
|
-- Step 8: Final safety check
|
||
|
DO $$
|
||
|
DECLARE
|
||
|
total_valid INTEGER;
|
||
|
BEGIN
|
||
|
SELECT COUNT(*) INTO total_valid
|
||
|
FROM messages
|
||
|
WHERE content IS NOT NULL AND metadata IS NOT NULL;
|
||
|
|
||
|
RAISE NOTICE 'Final check: % messages have valid data', total_valid;
|
||
|
END $$;
|