remdb 0.3.180__py3-none-any.whl → 0.3.258__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rem/agentic/README.md +36 -2
- rem/agentic/__init__.py +10 -1
- rem/agentic/context.py +185 -1
- rem/agentic/context_builder.py +56 -35
- rem/agentic/mcp/tool_wrapper.py +2 -2
- rem/agentic/providers/pydantic_ai.py +303 -111
- rem/agentic/schema.py +2 -2
- rem/api/main.py +1 -1
- rem/api/mcp_router/resources.py +223 -0
- rem/api/mcp_router/server.py +4 -0
- rem/api/mcp_router/tools.py +608 -166
- rem/api/routers/admin.py +30 -4
- rem/api/routers/auth.py +219 -20
- rem/api/routers/chat/child_streaming.py +393 -0
- rem/api/routers/chat/completions.py +77 -40
- rem/api/routers/chat/sse_events.py +7 -3
- rem/api/routers/chat/streaming.py +381 -291
- rem/api/routers/chat/streaming_utils.py +325 -0
- rem/api/routers/common.py +18 -0
- rem/api/routers/dev.py +7 -1
- rem/api/routers/feedback.py +11 -3
- rem/api/routers/messages.py +176 -38
- rem/api/routers/models.py +9 -1
- rem/api/routers/query.py +17 -15
- rem/api/routers/shared_sessions.py +16 -0
- rem/auth/jwt.py +19 -4
- rem/auth/middleware.py +42 -28
- rem/cli/README.md +62 -0
- rem/cli/commands/ask.py +205 -114
- rem/cli/commands/db.py +55 -31
- rem/cli/commands/experiments.py +1 -1
- rem/cli/commands/process.py +179 -43
- rem/cli/commands/query.py +109 -0
- rem/cli/commands/session.py +117 -0
- rem/cli/main.py +2 -0
- rem/models/core/experiment.py +1 -1
- rem/models/entities/ontology.py +18 -20
- rem/models/entities/session.py +1 -0
- rem/schemas/agents/core/agent-builder.yaml +1 -1
- rem/schemas/agents/rem.yaml +1 -1
- rem/schemas/agents/test_orchestrator.yaml +42 -0
- rem/schemas/agents/test_structured_output.yaml +52 -0
- rem/services/content/providers.py +151 -49
- rem/services/content/service.py +18 -5
- rem/services/embeddings/worker.py +26 -12
- rem/services/postgres/__init__.py +28 -3
- rem/services/postgres/diff_service.py +57 -5
- rem/services/postgres/programmable_diff_service.py +635 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +2 -2
- rem/services/postgres/register_type.py +11 -10
- rem/services/postgres/repository.py +39 -28
- rem/services/postgres/schema_generator.py +5 -5
- rem/services/postgres/sql_builder.py +6 -5
- rem/services/rem/README.md +4 -3
- rem/services/rem/parser.py +7 -10
- rem/services/rem/service.py +47 -0
- rem/services/session/__init__.py +8 -1
- rem/services/session/compression.py +47 -5
- rem/services/session/pydantic_messages.py +310 -0
- rem/services/session/reload.py +2 -1
- rem/settings.py +92 -7
- rem/sql/migrations/001_install.sql +125 -7
- rem/sql/migrations/002_install_models.sql +159 -149
- rem/sql/migrations/004_cache_system.sql +10 -276
- rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
- rem/utils/schema_loader.py +180 -120
- {remdb-0.3.180.dist-info → remdb-0.3.258.dist-info}/METADATA +7 -6
- {remdb-0.3.180.dist-info → remdb-0.3.258.dist-info}/RECORD +70 -61
- {remdb-0.3.180.dist-info → remdb-0.3.258.dist-info}/WHEEL +0 -0
- {remdb-0.3.180.dist-info → remdb-0.3.258.dist-info}/entry_points.txt +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
-- REM Cache System
|
|
2
|
-
-- Description:
|
|
2
|
+
-- Description: Cache management helpers for UNLOGGED tables (kv_store)
|
|
3
3
|
-- Version: 1.0.0
|
|
4
4
|
-- Date: 2025-11-29
|
|
5
5
|
--
|
|
6
6
|
-- This migration adds:
|
|
7
7
|
-- 1. cache_system_state table for debouncing and API secret storage
|
|
8
8
|
-- 2. maybe_trigger_kv_rebuild() function for async rebuild triggering
|
|
9
|
-
-- 3.
|
|
9
|
+
-- 3. Helper functions for cache management
|
|
10
10
|
--
|
|
11
|
-
--
|
|
12
|
-
--
|
|
13
|
-
-- Priority: pg_net (if available) → dblink (always available)
|
|
11
|
+
-- NOTE: Core functions (rem_lookup, rem_fuzzy, rem_traverse) are defined in 001_install.sql
|
|
12
|
+
-- This file only provides cache-specific infrastructure.
|
|
14
13
|
|
|
15
14
|
-- ============================================================================
|
|
16
15
|
-- REQUIRED EXTENSION
|
|
@@ -65,9 +64,11 @@ CREATE OR REPLACE FUNCTION rem_kv_store_empty(p_user_id TEXT)
|
|
|
65
64
|
RETURNS BOOLEAN AS $$
|
|
66
65
|
BEGIN
|
|
67
66
|
-- Quick existence check - very fast with index
|
|
67
|
+
-- Check for user-specific OR public (NULL user_id) entries
|
|
68
|
+
-- This ensures self-healing triggers correctly for public ontologies
|
|
68
69
|
RETURN NOT EXISTS (
|
|
69
70
|
SELECT 1 FROM kv_store
|
|
70
|
-
WHERE user_id = p_user_id
|
|
71
|
+
WHERE user_id = p_user_id OR user_id IS NULL
|
|
71
72
|
LIMIT 1
|
|
72
73
|
);
|
|
73
74
|
END;
|
|
@@ -194,271 +195,6 @@ $$ LANGUAGE plpgsql;
|
|
|
194
195
|
COMMENT ON FUNCTION maybe_trigger_kv_rebuild IS
|
|
195
196
|
'Async trigger for kv_store rebuild. Uses pg_net (API) or dblink (SQL). Includes debouncing.';
|
|
196
197
|
|
|
197
|
-
-- ============================================================================
|
|
198
|
-
-- UPDATED: rem_lookup with self-healing
|
|
199
|
-
-- ============================================================================
|
|
200
|
-
|
|
201
|
-
CREATE OR REPLACE FUNCTION rem_lookup(
|
|
202
|
-
p_entity_key VARCHAR(255),
|
|
203
|
-
p_tenant_id VARCHAR(100),
|
|
204
|
-
p_user_id VARCHAR(100)
|
|
205
|
-
)
|
|
206
|
-
RETURNS TABLE(
|
|
207
|
-
entity_type VARCHAR(100),
|
|
208
|
-
data JSONB
|
|
209
|
-
) AS $$
|
|
210
|
-
DECLARE
|
|
211
|
-
entity_table VARCHAR(100);
|
|
212
|
-
query_sql TEXT;
|
|
213
|
-
effective_user_id VARCHAR(100);
|
|
214
|
-
v_result_count INTEGER := 0;
|
|
215
|
-
BEGIN
|
|
216
|
-
effective_user_id := COALESCE(p_user_id, p_tenant_id);
|
|
217
|
-
|
|
218
|
-
-- First lookup in KV store to get entity_type (table name)
|
|
219
|
-
SELECT kv.entity_type INTO entity_table
|
|
220
|
-
FROM kv_store kv
|
|
221
|
-
WHERE (kv.user_id = effective_user_id OR kv.user_id IS NULL)
|
|
222
|
-
AND kv.entity_key = p_entity_key
|
|
223
|
-
LIMIT 1;
|
|
224
|
-
|
|
225
|
-
-- If not found, check if cache is empty and maybe trigger rebuild
|
|
226
|
-
IF entity_table IS NULL THEN
|
|
227
|
-
-- SELF-HEALING: Check if this is because cache is empty
|
|
228
|
-
IF rem_kv_store_empty(effective_user_id) THEN
|
|
229
|
-
PERFORM maybe_trigger_kv_rebuild(effective_user_id, 'rem_lookup');
|
|
230
|
-
END IF;
|
|
231
|
-
RETURN;
|
|
232
|
-
END IF;
|
|
233
|
-
|
|
234
|
-
-- Fetch raw record from underlying table as JSONB
|
|
235
|
-
query_sql := format('
|
|
236
|
-
SELECT
|
|
237
|
-
%L::VARCHAR(100) AS entity_type,
|
|
238
|
-
row_to_json(t)::jsonb AS data
|
|
239
|
-
FROM %I t
|
|
240
|
-
WHERE (t.user_id = $1 OR t.user_id IS NULL)
|
|
241
|
-
AND t.name = $2
|
|
242
|
-
AND t.deleted_at IS NULL
|
|
243
|
-
', entity_table, entity_table);
|
|
244
|
-
|
|
245
|
-
RETURN QUERY EXECUTE query_sql USING effective_user_id, p_entity_key;
|
|
246
|
-
END;
|
|
247
|
-
$$ LANGUAGE plpgsql STABLE;
|
|
248
|
-
|
|
249
|
-
-- ============================================================================
|
|
250
|
-
-- UPDATED: rem_fuzzy with self-healing
|
|
251
|
-
-- ============================================================================
|
|
252
|
-
|
|
253
|
-
CREATE OR REPLACE FUNCTION rem_fuzzy(
|
|
254
|
-
p_query TEXT,
|
|
255
|
-
p_tenant_id VARCHAR(100),
|
|
256
|
-
p_threshold REAL DEFAULT 0.3,
|
|
257
|
-
p_limit INTEGER DEFAULT 10,
|
|
258
|
-
p_user_id VARCHAR(100) DEFAULT NULL
|
|
259
|
-
)
|
|
260
|
-
RETURNS TABLE(
|
|
261
|
-
entity_type VARCHAR(100),
|
|
262
|
-
similarity_score REAL,
|
|
263
|
-
data JSONB
|
|
264
|
-
) AS $$
|
|
265
|
-
DECLARE
|
|
266
|
-
kv_matches RECORD;
|
|
267
|
-
entities_by_table JSONB := '{}'::jsonb;
|
|
268
|
-
table_keys JSONB;
|
|
269
|
-
effective_user_id VARCHAR(100);
|
|
270
|
-
v_found_any BOOLEAN := FALSE;
|
|
271
|
-
BEGIN
|
|
272
|
-
effective_user_id := COALESCE(p_user_id, p_tenant_id);
|
|
273
|
-
|
|
274
|
-
-- Find matching keys in KV store
|
|
275
|
-
FOR kv_matches IN
|
|
276
|
-
SELECT
|
|
277
|
-
kv.entity_key,
|
|
278
|
-
kv.entity_type,
|
|
279
|
-
similarity(kv.entity_key, p_query) AS sim_score
|
|
280
|
-
FROM kv_store kv
|
|
281
|
-
WHERE (kv.user_id = effective_user_id OR kv.user_id IS NULL)
|
|
282
|
-
AND kv.entity_key % p_query
|
|
283
|
-
AND similarity(kv.entity_key, p_query) >= p_threshold
|
|
284
|
-
ORDER BY sim_score DESC
|
|
285
|
-
LIMIT p_limit
|
|
286
|
-
LOOP
|
|
287
|
-
v_found_any := TRUE;
|
|
288
|
-
-- Build JSONB mapping {table: [keys]}
|
|
289
|
-
IF entities_by_table ? kv_matches.entity_type THEN
|
|
290
|
-
table_keys := entities_by_table->kv_matches.entity_type;
|
|
291
|
-
entities_by_table := jsonb_set(
|
|
292
|
-
entities_by_table,
|
|
293
|
-
ARRAY[kv_matches.entity_type],
|
|
294
|
-
table_keys || jsonb_build_array(kv_matches.entity_key)
|
|
295
|
-
);
|
|
296
|
-
ELSE
|
|
297
|
-
entities_by_table := jsonb_set(
|
|
298
|
-
entities_by_table,
|
|
299
|
-
ARRAY[kv_matches.entity_type],
|
|
300
|
-
jsonb_build_array(kv_matches.entity_key)
|
|
301
|
-
);
|
|
302
|
-
END IF;
|
|
303
|
-
END LOOP;
|
|
304
|
-
|
|
305
|
-
-- SELF-HEALING: If no matches and cache is empty, trigger rebuild
|
|
306
|
-
IF NOT v_found_any AND rem_kv_store_empty(effective_user_id) THEN
|
|
307
|
-
PERFORM maybe_trigger_kv_rebuild(effective_user_id, 'rem_fuzzy');
|
|
308
|
-
END IF;
|
|
309
|
-
|
|
310
|
-
-- Fetch full records
|
|
311
|
-
RETURN QUERY
|
|
312
|
-
SELECT
|
|
313
|
-
f.entity_type::VARCHAR(100),
|
|
314
|
-
similarity(f.entity_key, p_query) AS similarity_score,
|
|
315
|
-
f.entity_record AS data
|
|
316
|
-
FROM rem_fetch(entities_by_table, effective_user_id) f
|
|
317
|
-
ORDER BY similarity_score DESC;
|
|
318
|
-
END;
|
|
319
|
-
$$ LANGUAGE plpgsql STABLE;
|
|
320
|
-
|
|
321
|
-
-- ============================================================================
|
|
322
|
-
-- UPDATED: rem_traverse with self-healing
|
|
323
|
-
-- ============================================================================
|
|
324
|
-
|
|
325
|
-
CREATE OR REPLACE FUNCTION rem_traverse(
|
|
326
|
-
p_entity_key VARCHAR(255),
|
|
327
|
-
p_tenant_id VARCHAR(100),
|
|
328
|
-
p_user_id VARCHAR(100),
|
|
329
|
-
p_max_depth INTEGER DEFAULT 1,
|
|
330
|
-
p_rel_type VARCHAR(100) DEFAULT NULL,
|
|
331
|
-
p_keys_only BOOLEAN DEFAULT FALSE
|
|
332
|
-
)
|
|
333
|
-
RETURNS TABLE(
|
|
334
|
-
depth INTEGER,
|
|
335
|
-
entity_key VARCHAR(255),
|
|
336
|
-
entity_type VARCHAR(100),
|
|
337
|
-
entity_id UUID,
|
|
338
|
-
rel_type VARCHAR(100),
|
|
339
|
-
rel_weight REAL,
|
|
340
|
-
path TEXT[],
|
|
341
|
-
entity_record JSONB
|
|
342
|
-
) AS $$
|
|
343
|
-
DECLARE
|
|
344
|
-
graph_keys RECORD;
|
|
345
|
-
entities_by_table JSONB := '{}'::jsonb;
|
|
346
|
-
table_keys JSONB;
|
|
347
|
-
effective_user_id VARCHAR(100);
|
|
348
|
-
v_found_start BOOLEAN := FALSE;
|
|
349
|
-
BEGIN
|
|
350
|
-
effective_user_id := COALESCE(p_user_id, p_tenant_id);
|
|
351
|
-
|
|
352
|
-
-- Check if start entity exists in kv_store
|
|
353
|
-
SELECT TRUE INTO v_found_start
|
|
354
|
-
FROM kv_store kv
|
|
355
|
-
WHERE (kv.user_id = effective_user_id OR kv.user_id IS NULL)
|
|
356
|
-
AND kv.entity_key = p_entity_key
|
|
357
|
-
LIMIT 1;
|
|
358
|
-
|
|
359
|
-
-- SELF-HEALING: If start not found and cache is empty, trigger rebuild
|
|
360
|
-
IF NOT COALESCE(v_found_start, FALSE) THEN
|
|
361
|
-
IF rem_kv_store_empty(effective_user_id) THEN
|
|
362
|
-
PERFORM maybe_trigger_kv_rebuild(effective_user_id, 'rem_traverse');
|
|
363
|
-
END IF;
|
|
364
|
-
RETURN;
|
|
365
|
-
END IF;
|
|
366
|
-
|
|
367
|
-
-- Original traverse logic
|
|
368
|
-
FOR graph_keys IN
|
|
369
|
-
WITH RECURSIVE graph_traversal AS (
|
|
370
|
-
SELECT
|
|
371
|
-
0 AS depth,
|
|
372
|
-
kv.entity_key,
|
|
373
|
-
kv.entity_type,
|
|
374
|
-
kv.entity_id,
|
|
375
|
-
NULL::VARCHAR(100) AS rel_type,
|
|
376
|
-
NULL::REAL AS rel_weight,
|
|
377
|
-
ARRAY[kv.entity_key]::TEXT[] AS path
|
|
378
|
-
FROM kv_store kv
|
|
379
|
-
WHERE (kv.user_id = effective_user_id OR kv.user_id IS NULL)
|
|
380
|
-
AND kv.entity_key = p_entity_key
|
|
381
|
-
|
|
382
|
-
UNION ALL
|
|
383
|
-
|
|
384
|
-
SELECT
|
|
385
|
-
gt.depth + 1,
|
|
386
|
-
target_kv.entity_key,
|
|
387
|
-
target_kv.entity_type,
|
|
388
|
-
target_kv.entity_id,
|
|
389
|
-
(edge->>'rel_type')::VARCHAR(100) AS rel_type,
|
|
390
|
-
COALESCE((edge->>'weight')::REAL, 1.0) AS rel_weight,
|
|
391
|
-
gt.path || target_kv.entity_key AS path
|
|
392
|
-
FROM graph_traversal gt
|
|
393
|
-
JOIN kv_store source_kv ON source_kv.entity_key = gt.entity_key
|
|
394
|
-
AND (source_kv.user_id = effective_user_id OR source_kv.user_id IS NULL)
|
|
395
|
-
CROSS JOIN LATERAL jsonb_array_elements(COALESCE(source_kv.graph_edges, '[]'::jsonb)) AS edge
|
|
396
|
-
JOIN kv_store target_kv ON target_kv.entity_key = (edge->>'dst')::VARCHAR(255)
|
|
397
|
-
AND (target_kv.user_id = effective_user_id OR target_kv.user_id IS NULL)
|
|
398
|
-
WHERE gt.depth < p_max_depth
|
|
399
|
-
AND (p_rel_type IS NULL OR (edge->>'rel_type')::VARCHAR(100) = p_rel_type)
|
|
400
|
-
AND NOT (target_kv.entity_key = ANY(gt.path))
|
|
401
|
-
)
|
|
402
|
-
SELECT DISTINCT ON (gt.entity_key)
|
|
403
|
-
gt.depth,
|
|
404
|
-
gt.entity_key,
|
|
405
|
-
gt.entity_type,
|
|
406
|
-
gt.entity_id,
|
|
407
|
-
gt.rel_type,
|
|
408
|
-
gt.rel_weight,
|
|
409
|
-
gt.path
|
|
410
|
-
FROM graph_traversal gt
|
|
411
|
-
WHERE gt.depth > 0
|
|
412
|
-
ORDER BY gt.entity_key, gt.depth
|
|
413
|
-
LOOP
|
|
414
|
-
IF p_keys_only THEN
|
|
415
|
-
depth := graph_keys.depth;
|
|
416
|
-
entity_key := graph_keys.entity_key;
|
|
417
|
-
entity_type := graph_keys.entity_type;
|
|
418
|
-
entity_id := graph_keys.entity_id;
|
|
419
|
-
rel_type := graph_keys.rel_type;
|
|
420
|
-
rel_weight := graph_keys.rel_weight;
|
|
421
|
-
path := graph_keys.path;
|
|
422
|
-
entity_record := NULL;
|
|
423
|
-
RETURN NEXT;
|
|
424
|
-
ELSE
|
|
425
|
-
IF entities_by_table ? graph_keys.entity_type THEN
|
|
426
|
-
table_keys := entities_by_table->graph_keys.entity_type;
|
|
427
|
-
entities_by_table := jsonb_set(
|
|
428
|
-
entities_by_table,
|
|
429
|
-
ARRAY[graph_keys.entity_type],
|
|
430
|
-
table_keys || jsonb_build_array(graph_keys.entity_key)
|
|
431
|
-
);
|
|
432
|
-
ELSE
|
|
433
|
-
entities_by_table := jsonb_set(
|
|
434
|
-
entities_by_table,
|
|
435
|
-
ARRAY[graph_keys.entity_type],
|
|
436
|
-
jsonb_build_array(graph_keys.entity_key)
|
|
437
|
-
);
|
|
438
|
-
END IF;
|
|
439
|
-
END IF;
|
|
440
|
-
END LOOP;
|
|
441
|
-
|
|
442
|
-
IF NOT p_keys_only THEN
|
|
443
|
-
RETURN QUERY
|
|
444
|
-
SELECT
|
|
445
|
-
g.depth,
|
|
446
|
-
g.entity_key,
|
|
447
|
-
g.entity_type,
|
|
448
|
-
g.entity_id,
|
|
449
|
-
g.rel_type,
|
|
450
|
-
g.rel_weight,
|
|
451
|
-
g.path,
|
|
452
|
-
f.entity_record
|
|
453
|
-
FROM (
|
|
454
|
-
SELECT * FROM rem_traverse(p_entity_key, p_tenant_id, effective_user_id, p_max_depth, p_rel_type, TRUE)
|
|
455
|
-
) g
|
|
456
|
-
LEFT JOIN rem_fetch(entities_by_table, effective_user_id) f
|
|
457
|
-
ON g.entity_key = f.entity_key;
|
|
458
|
-
END IF;
|
|
459
|
-
END;
|
|
460
|
-
$$ LANGUAGE plpgsql STABLE;
|
|
461
|
-
|
|
462
198
|
-- ============================================================================
|
|
463
199
|
-- HELPER: Get API secret for validation
|
|
464
200
|
-- ============================================================================
|
|
@@ -527,9 +263,9 @@ BEGIN
|
|
|
527
263
|
RAISE NOTICE '';
|
|
528
264
|
RAISE NOTICE 'Functions:';
|
|
529
265
|
RAISE NOTICE ' maybe_trigger_kv_rebuild() - Async rebuild trigger';
|
|
530
|
-
RAISE NOTICE '
|
|
531
|
-
RAISE NOTICE '
|
|
532
|
-
RAISE NOTICE '
|
|
266
|
+
RAISE NOTICE ' rem_kv_store_empty() - Check if cache is empty';
|
|
267
|
+
RAISE NOTICE ' rem_get_cache_api_secret() - Get API secret';
|
|
268
|
+
RAISE NOTICE ' rem_record_cache_rebuild() - Record rebuild completion';
|
|
533
269
|
RAISE NOTICE '';
|
|
534
270
|
RAISE NOTICE 'Async Methods Available:';
|
|
535
271
|
IF v_has_pgnet THEN
|
|
@@ -542,7 +278,5 @@ BEGIN
|
|
|
542
278
|
ELSE
|
|
543
279
|
RAISE NOTICE ' [ ] dblink - Not installed';
|
|
544
280
|
END IF;
|
|
545
|
-
RAISE NOTICE '';
|
|
546
|
-
RAISE NOTICE 'Self-Healing: Queries will auto-trigger rebuild on empty cache';
|
|
547
281
|
RAISE NOTICE '============================================================';
|
|
548
282
|
END $$;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
-- Migration: Update messages.session_id from session name to session UUID
|
|
2
|
+
-- This fixes the bug where messages were stored with session.name instead of session.id
|
|
3
|
+
--
|
|
4
|
+
-- Run this migration AFTER deploying the code fixes in remdb 0.3.204+
|
|
5
|
+
-- The code now correctly stores session.id (UUID), but existing data needs migration.
|
|
6
|
+
|
|
7
|
+
BEGIN;
|
|
8
|
+
|
|
9
|
+
-- First, count how many messages need to be updated
|
|
10
|
+
DO $$
|
|
11
|
+
DECLARE
|
|
12
|
+
count_to_migrate INTEGER;
|
|
13
|
+
BEGIN
|
|
14
|
+
SELECT COUNT(*) INTO count_to_migrate
|
|
15
|
+
FROM messages m
|
|
16
|
+
JOIN sessions s ON m.session_id = s.name
|
|
17
|
+
WHERE m.session_id != s.id::text;
|
|
18
|
+
|
|
19
|
+
RAISE NOTICE 'Messages needing migration: %', count_to_migrate;
|
|
20
|
+
END $$;
|
|
21
|
+
|
|
22
|
+
-- Update messages.session_id from session name to session UUID
|
|
23
|
+
UPDATE messages m
|
|
24
|
+
SET session_id = s.id::text
|
|
25
|
+
FROM sessions s
|
|
26
|
+
WHERE m.session_id = s.name
|
|
27
|
+
AND m.session_id != s.id::text;
|
|
28
|
+
|
|
29
|
+
-- Report how many were updated
|
|
30
|
+
DO $$
|
|
31
|
+
DECLARE
|
|
32
|
+
updated_count INTEGER;
|
|
33
|
+
BEGIN
|
|
34
|
+
GET DIAGNOSTICS updated_count = ROW_COUNT;
|
|
35
|
+
RAISE NOTICE 'Messages updated: %', updated_count;
|
|
36
|
+
END $$;
|
|
37
|
+
|
|
38
|
+
COMMIT;
|
|
39
|
+
|
|
40
|
+
-- Verify the fix - all messages should now join by UUID
|
|
41
|
+
SELECT
|
|
42
|
+
'Messages matching sessions by UUID' as status,
|
|
43
|
+
COUNT(*) as count
|
|
44
|
+
FROM messages m
|
|
45
|
+
JOIN sessions s ON m.session_id = s.id::text;
|