@axiom-lattice/pg-stores 1.0.21 → 1.0.23
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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +17 -0
- package/dist/index.d.mts +126 -58
- package/dist/index.d.ts +126 -58
- package/dist/index.js +551 -135
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +544 -135
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +7 -0
- package/src/migrations/assistant_migrations.ts +4 -2
- package/src/migrations/assistant_pk_migration.ts +101 -0
- package/src/migrations/assistant_tenant_migration.ts +29 -0
- package/src/migrations/schedule_tenant_migration.ts +75 -0
- package/src/migrations/skill_pk_migration.ts +97 -0
- package/src/migrations/skill_tenant_migration.ts +55 -0
- package/src/migrations/thread_pk_migration.ts +99 -0
- package/src/migrations/thread_tenant_migration.ts +55 -0
- package/src/stores/PostgreSQLAssistantStore.ts +55 -22
- package/src/stores/PostgreSQLScheduleStorage.ts +16 -0
- package/src/stores/PostgreSQLSkillStore.ts +146 -103
- package/src/stores/PostgreSQLThreadStore.ts +40 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiom-lattice/pg-stores",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"description": "PG stores implementation for Axiom Lattice framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"pg": "^8.16.3",
|
|
24
|
-
"@axiom-lattice/protocols": "2.1.
|
|
25
|
-
"@axiom-lattice/core": "2.1.
|
|
24
|
+
"@axiom-lattice/protocols": "2.1.20",
|
|
25
|
+
"@axiom-lattice/core": "2.1.33"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^20.11.24",
|
package/src/index.ts
CHANGED
|
@@ -18,9 +18,16 @@ export * from "./stores/PostgreSQLTenantStore";
|
|
|
18
18
|
export * from "./stores/PostgreSQLUserTenantLinkStore";
|
|
19
19
|
export * from "./migrations/migration";
|
|
20
20
|
export * from "./migrations/thread_migrations";
|
|
21
|
+
export * from "./migrations/thread_tenant_migration";
|
|
22
|
+
export * from "./migrations/thread_pk_migration";
|
|
21
23
|
export * from "./migrations/assistant_migrations";
|
|
24
|
+
export * from "./migrations/assistant_tenant_migration";
|
|
25
|
+
export * from "./migrations/assistant_pk_migration";
|
|
22
26
|
export * from "./migrations/schedule_migrations";
|
|
27
|
+
export * from "./migrations/schedule_tenant_migration";
|
|
23
28
|
export * from "./migrations/skill_migrations";
|
|
29
|
+
export * from "./migrations/skill_tenant_migration";
|
|
30
|
+
export * from "./migrations/skill_pk_migration";
|
|
24
31
|
export * from "./migrations/database_config_migrations";
|
|
25
32
|
export * from "./migrations/metrics_config_migrations";
|
|
26
33
|
export * from "./migrations/mcp_server_config_migrations";
|
|
@@ -14,12 +14,14 @@ export const createAssistantsTable: Migration = {
|
|
|
14
14
|
up: async (client: PoolClient) => {
|
|
15
15
|
await client.query(`
|
|
16
16
|
CREATE TABLE IF NOT EXISTS lattice_assistants (
|
|
17
|
-
id VARCHAR(255)
|
|
17
|
+
id VARCHAR(255) NOT NULL,
|
|
18
|
+
tenant_id VARCHAR(255) NOT NULL DEFAULT 'default',
|
|
18
19
|
name VARCHAR(255) NOT NULL,
|
|
19
20
|
description TEXT,
|
|
20
21
|
graph_definition JSONB NOT NULL,
|
|
21
22
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
22
|
-
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
23
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
24
|
+
PRIMARY KEY (tenant_id, id)
|
|
23
25
|
)
|
|
24
26
|
`);
|
|
25
27
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assistant table primary key migration
|
|
3
|
+
* Changes primary key from (id) to (tenant_id, id) for multi-tenancy
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PoolClient } from "pg";
|
|
7
|
+
import { Migration } from "./migration";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Migration: Change primary key to support multi-tenancy
|
|
11
|
+
*/
|
|
12
|
+
export const changeAssistantPrimaryKey: Migration = {
|
|
13
|
+
version: 15,
|
|
14
|
+
name: "change_assistant_primary_key",
|
|
15
|
+
up: async (client: PoolClient) => {
|
|
16
|
+
// Check if table exists and has the old primary key structure
|
|
17
|
+
const tableExists = await client.query(`
|
|
18
|
+
SELECT EXISTS (
|
|
19
|
+
SELECT FROM information_schema.tables
|
|
20
|
+
WHERE table_name = 'lattice_assistants'
|
|
21
|
+
)
|
|
22
|
+
`);
|
|
23
|
+
|
|
24
|
+
if (!tableExists.rows[0].exists) {
|
|
25
|
+
// Table doesn't exist, it will be created by createAssistantsTable migration
|
|
26
|
+
// with the correct primary key (tenant_id, id)
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check current primary key
|
|
31
|
+
const pkResult = await client.query(`
|
|
32
|
+
SELECT a.attname as column_name
|
|
33
|
+
FROM pg_index i
|
|
34
|
+
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
|
|
35
|
+
WHERE i.indrelid = 'lattice_assistants'::regclass
|
|
36
|
+
AND i.indisprimary
|
|
37
|
+
`);
|
|
38
|
+
|
|
39
|
+
const pkColumns = pkResult.rows.map(row => row.column_name);
|
|
40
|
+
|
|
41
|
+
// If primary key is already (tenant_id, id), nothing to do
|
|
42
|
+
if (pkColumns.includes('tenant_id') && pkColumns.includes('id')) {
|
|
43
|
+
console.log('Primary key already correct (tenant_id, id)');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// If primary key is just (id), we need to change it
|
|
48
|
+
if (pkColumns.length === 1 && pkColumns[0] === 'id') {
|
|
49
|
+
console.log('Changing primary key from (id) to (tenant_id, id)');
|
|
50
|
+
|
|
51
|
+
// Check if tenant_id column exists
|
|
52
|
+
const tenantIdExists = await client.query(`
|
|
53
|
+
SELECT EXISTS (
|
|
54
|
+
SELECT FROM information_schema.columns
|
|
55
|
+
WHERE table_name = 'lattice_assistants'
|
|
56
|
+
AND column_name = 'tenant_id'
|
|
57
|
+
)
|
|
58
|
+
`);
|
|
59
|
+
|
|
60
|
+
if (!tenantIdExists.rows[0].exists) {
|
|
61
|
+
// Add tenant_id column if it doesn't exist
|
|
62
|
+
await client.query(`
|
|
63
|
+
ALTER TABLE lattice_assistants
|
|
64
|
+
ADD COLUMN tenant_id VARCHAR(255) DEFAULT 'default'
|
|
65
|
+
`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Update existing records to have tenant_id
|
|
69
|
+
await client.query(`
|
|
70
|
+
UPDATE lattice_assistants
|
|
71
|
+
SET tenant_id = 'default'
|
|
72
|
+
WHERE tenant_id IS NULL
|
|
73
|
+
`);
|
|
74
|
+
|
|
75
|
+
// Make tenant_id NOT NULL
|
|
76
|
+
await client.query(`
|
|
77
|
+
ALTER TABLE lattice_assistants
|
|
78
|
+
ALTER COLUMN tenant_id SET NOT NULL
|
|
79
|
+
`);
|
|
80
|
+
|
|
81
|
+
// Drop the old primary key
|
|
82
|
+
await client.query(`
|
|
83
|
+
ALTER TABLE lattice_assistants
|
|
84
|
+
DROP CONSTRAINT IF EXISTS lattice_assistants_pkey
|
|
85
|
+
`);
|
|
86
|
+
|
|
87
|
+
// Add new composite primary key
|
|
88
|
+
await client.query(`
|
|
89
|
+
ALTER TABLE lattice_assistants
|
|
90
|
+
ADD PRIMARY KEY (tenant_id, id)
|
|
91
|
+
`);
|
|
92
|
+
|
|
93
|
+
console.log('Primary key changed successfully to (tenant_id, id)');
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
down: async (client: PoolClient) => {
|
|
97
|
+
// Reverting this is complex and potentially dangerous
|
|
98
|
+
// as it would require ensuring no duplicate IDs across tenants
|
|
99
|
+
console.warn('Down migration for changeAssistantPrimaryKey is not supported');
|
|
100
|
+
},
|
|
101
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assistant table tenant isolation migration
|
|
3
|
+
* Adds tenant_id column to lattice_assistants table
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PoolClient } from "pg";
|
|
7
|
+
import { Migration } from "./migration";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Migration: Add tenant_id column to assistants table
|
|
11
|
+
*/
|
|
12
|
+
export const addAssistantTenantId: Migration = {
|
|
13
|
+
version: 14,
|
|
14
|
+
name: "add_assistant_tenant_id",
|
|
15
|
+
up: async (client: PoolClient) => {
|
|
16
|
+
// tenant_id column is now created in the initial migration
|
|
17
|
+
// This migration is kept for backward compatibility but does nothing
|
|
18
|
+
// Create index for tenant-based queries if not exists
|
|
19
|
+
await client.query(`
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_assistants_tenant_id
|
|
21
|
+
ON lattice_assistants(tenant_id)
|
|
22
|
+
`);
|
|
23
|
+
},
|
|
24
|
+
down: async (client: PoolClient) => {
|
|
25
|
+
await client.query(
|
|
26
|
+
"DROP INDEX IF EXISTS idx_lattice_assistants_tenant_id"
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schedule table tenant isolation migration
|
|
3
|
+
* Adds tenant_id column to lattice_scheduled_tasks table
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PoolClient } from "pg";
|
|
7
|
+
import { Migration } from "./migration";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Migration: Add tenant_id column to scheduled tasks table
|
|
11
|
+
*/
|
|
12
|
+
export const addScheduleTenantId: Migration = {
|
|
13
|
+
version: 18,
|
|
14
|
+
name: "add_schedule_tenant_id",
|
|
15
|
+
up: async (client: PoolClient) => {
|
|
16
|
+
// Add tenant_id column
|
|
17
|
+
await client.query(`
|
|
18
|
+
ALTER TABLE lattice_scheduled_tasks
|
|
19
|
+
ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(255) DEFAULT 'default'
|
|
20
|
+
`);
|
|
21
|
+
|
|
22
|
+
// Update existing records to have tenant_id
|
|
23
|
+
await client.query(`
|
|
24
|
+
UPDATE lattice_scheduled_tasks
|
|
25
|
+
SET tenant_id = 'default'
|
|
26
|
+
WHERE tenant_id IS NULL
|
|
27
|
+
`);
|
|
28
|
+
|
|
29
|
+
// Make tenant_id NOT NULL after setting defaults
|
|
30
|
+
await client.query(`
|
|
31
|
+
ALTER TABLE lattice_scheduled_tasks
|
|
32
|
+
ALTER COLUMN tenant_id SET NOT NULL
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
// Drop the default constraint
|
|
36
|
+
await client.query(`
|
|
37
|
+
ALTER TABLE lattice_scheduled_tasks
|
|
38
|
+
ALTER COLUMN tenant_id DROP DEFAULT
|
|
39
|
+
`);
|
|
40
|
+
|
|
41
|
+
// Create index for tenant-based queries
|
|
42
|
+
await client.query(`
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_tenant_id
|
|
44
|
+
ON lattice_scheduled_tasks(tenant_id)
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
// Create composite index for common tenant+assistant queries
|
|
48
|
+
await client.query(`
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_tenant_assistant
|
|
50
|
+
ON lattice_scheduled_tasks(tenant_id, assistant_id)
|
|
51
|
+
WHERE assistant_id IS NOT NULL
|
|
52
|
+
`);
|
|
53
|
+
|
|
54
|
+
// Create composite index for common tenant+thread queries
|
|
55
|
+
await client.query(`
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_tenant_thread
|
|
57
|
+
ON lattice_scheduled_tasks(tenant_id, thread_id)
|
|
58
|
+
WHERE thread_id IS NOT NULL
|
|
59
|
+
`);
|
|
60
|
+
},
|
|
61
|
+
down: async (client: PoolClient) => {
|
|
62
|
+
await client.query(
|
|
63
|
+
"DROP INDEX IF EXISTS idx_scheduled_tasks_tenant_thread"
|
|
64
|
+
);
|
|
65
|
+
await client.query(
|
|
66
|
+
"DROP INDEX IF EXISTS idx_scheduled_tasks_tenant_assistant"
|
|
67
|
+
);
|
|
68
|
+
await client.query(
|
|
69
|
+
"DROP INDEX IF EXISTS idx_scheduled_tasks_tenant_id"
|
|
70
|
+
);
|
|
71
|
+
await client.query(
|
|
72
|
+
"ALTER TABLE lattice_scheduled_tasks DROP COLUMN IF EXISTS tenant_id"
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill table primary key migration
|
|
3
|
+
* Changes primary key from (id) to (tenant_id, id) for multi-tenancy
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PoolClient } from "pg";
|
|
7
|
+
import { Migration } from "./migration";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Migration: Change skill table primary key to support multi-tenancy
|
|
11
|
+
*/
|
|
12
|
+
export const changeSkillPrimaryKey: Migration = {
|
|
13
|
+
version: 17,
|
|
14
|
+
name: "change_skill_primary_key",
|
|
15
|
+
up: async (client: PoolClient) => {
|
|
16
|
+
// Check if table exists
|
|
17
|
+
const tableExists = await client.query(`
|
|
18
|
+
SELECT EXISTS (
|
|
19
|
+
SELECT FROM information_schema.tables
|
|
20
|
+
WHERE table_name = 'lattice_skills'
|
|
21
|
+
)
|
|
22
|
+
`);
|
|
23
|
+
|
|
24
|
+
if (!tableExists.rows[0].exists) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check current primary key
|
|
29
|
+
const pkResult = await client.query(`
|
|
30
|
+
SELECT a.attname as column_name
|
|
31
|
+
FROM pg_index i
|
|
32
|
+
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
|
|
33
|
+
WHERE i.indrelid = 'lattice_skills'::regclass
|
|
34
|
+
AND i.indisprimary
|
|
35
|
+
`);
|
|
36
|
+
|
|
37
|
+
const pkColumns = pkResult.rows.map(row => row.column_name);
|
|
38
|
+
|
|
39
|
+
// If primary key is already (tenant_id, id), nothing to do
|
|
40
|
+
if (pkColumns.includes('tenant_id') && pkColumns.includes('id')) {
|
|
41
|
+
console.log('Skill table primary key already correct (tenant_id, id)');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// If primary key is just (id), we need to change it
|
|
46
|
+
if (pkColumns.length === 1 && pkColumns[0] === 'id') {
|
|
47
|
+
console.log('Changing skill table primary key from (id) to (tenant_id, id)');
|
|
48
|
+
|
|
49
|
+
// Check if tenant_id column exists
|
|
50
|
+
const tenantIdExists = await client.query(`
|
|
51
|
+
SELECT EXISTS (
|
|
52
|
+
SELECT FROM information_schema.columns
|
|
53
|
+
WHERE table_name = 'lattice_skills'
|
|
54
|
+
AND column_name = 'tenant_id'
|
|
55
|
+
)
|
|
56
|
+
`);
|
|
57
|
+
|
|
58
|
+
if (!tenantIdExists.rows[0].exists) {
|
|
59
|
+
// Add tenant_id column if it doesn't exist
|
|
60
|
+
await client.query(`
|
|
61
|
+
ALTER TABLE lattice_skills
|
|
62
|
+
ADD COLUMN tenant_id VARCHAR(255) DEFAULT 'default'
|
|
63
|
+
`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Update existing records to have tenant_id
|
|
67
|
+
await client.query(`
|
|
68
|
+
UPDATE lattice_skills
|
|
69
|
+
SET tenant_id = 'default'
|
|
70
|
+
WHERE tenant_id IS NULL
|
|
71
|
+
`);
|
|
72
|
+
|
|
73
|
+
// Make tenant_id NOT NULL
|
|
74
|
+
await client.query(`
|
|
75
|
+
ALTER TABLE lattice_skills
|
|
76
|
+
ALTER COLUMN tenant_id SET NOT NULL
|
|
77
|
+
`);
|
|
78
|
+
|
|
79
|
+
// Drop the old primary key
|
|
80
|
+
await client.query(`
|
|
81
|
+
ALTER TABLE lattice_skills
|
|
82
|
+
DROP CONSTRAINT IF EXISTS lattice_skills_pkey
|
|
83
|
+
`);
|
|
84
|
+
|
|
85
|
+
// Add new composite primary key
|
|
86
|
+
await client.query(`
|
|
87
|
+
ALTER TABLE lattice_skills
|
|
88
|
+
ADD PRIMARY KEY (tenant_id, id)
|
|
89
|
+
`);
|
|
90
|
+
|
|
91
|
+
console.log('Skill table primary key changed successfully to (tenant_id, id)');
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
down: async (client: PoolClient) => {
|
|
95
|
+
console.warn('Down migration for changeSkillPrimaryKey is not supported');
|
|
96
|
+
},
|
|
97
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill table tenant isolation migration
|
|
3
|
+
* Adds tenant_id column to lattice_skills table
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PoolClient } from "pg";
|
|
7
|
+
import { Migration } from "./migration";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Migration: Add tenant_id column to skills table
|
|
11
|
+
*/
|
|
12
|
+
export const addSkillTenantId: Migration = {
|
|
13
|
+
version: 16,
|
|
14
|
+
name: "add_skill_tenant_id",
|
|
15
|
+
up: async (client: PoolClient) => {
|
|
16
|
+
// Add tenant_id column
|
|
17
|
+
await client.query(`
|
|
18
|
+
ALTER TABLE lattice_skills
|
|
19
|
+
ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(255) DEFAULT 'default'
|
|
20
|
+
`);
|
|
21
|
+
|
|
22
|
+
// Update existing records to have tenant_id
|
|
23
|
+
await client.query(`
|
|
24
|
+
UPDATE lattice_skills
|
|
25
|
+
SET tenant_id = 'default'
|
|
26
|
+
WHERE tenant_id IS NULL
|
|
27
|
+
`);
|
|
28
|
+
|
|
29
|
+
// Make tenant_id NOT NULL after setting defaults
|
|
30
|
+
await client.query(`
|
|
31
|
+
ALTER TABLE lattice_skills
|
|
32
|
+
ALTER COLUMN tenant_id SET NOT NULL
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
// Drop the default constraint
|
|
36
|
+
await client.query(`
|
|
37
|
+
ALTER TABLE lattice_skills
|
|
38
|
+
ALTER COLUMN tenant_id DROP DEFAULT
|
|
39
|
+
`);
|
|
40
|
+
|
|
41
|
+
// Create index for tenant-based queries
|
|
42
|
+
await client.query(`
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_skills_tenant_id
|
|
44
|
+
ON lattice_skills(tenant_id)
|
|
45
|
+
`);
|
|
46
|
+
},
|
|
47
|
+
down: async (client: PoolClient) => {
|
|
48
|
+
await client.query(
|
|
49
|
+
"DROP INDEX IF EXISTS idx_lattice_skills_tenant_id"
|
|
50
|
+
);
|
|
51
|
+
await client.query(
|
|
52
|
+
"ALTER TABLE lattice_skills DROP COLUMN IF EXISTS tenant_id"
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thread table primary key migration
|
|
3
|
+
* Changes primary key from (id, assistant_id) to (id, tenant_id) for multi-tenancy
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PoolClient } from "pg";
|
|
7
|
+
import { Migration } from "./migration";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Migration: Change primary key to support multi-tenancy
|
|
11
|
+
*/
|
|
12
|
+
export const changeThreadPrimaryKey: Migration = {
|
|
13
|
+
version: 19,
|
|
14
|
+
name: "change_thread_primary_key",
|
|
15
|
+
up: async (client: PoolClient) => {
|
|
16
|
+
// Check if table exists
|
|
17
|
+
const tableExists = await client.query(`
|
|
18
|
+
SELECT EXISTS (
|
|
19
|
+
SELECT FROM information_schema.tables
|
|
20
|
+
WHERE table_name = 'lattice_threads'
|
|
21
|
+
)
|
|
22
|
+
`);
|
|
23
|
+
|
|
24
|
+
if (!tableExists.rows[0].exists) {
|
|
25
|
+
// Table doesn't exist, it will be created with correct schema
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check current primary key
|
|
30
|
+
const pkResult = await client.query(`
|
|
31
|
+
SELECT a.attname as column_name
|
|
32
|
+
FROM pg_index i
|
|
33
|
+
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
|
|
34
|
+
WHERE i.indrelid = 'lattice_threads'::regclass
|
|
35
|
+
AND i.indisprimary
|
|
36
|
+
`);
|
|
37
|
+
|
|
38
|
+
const pkColumns = pkResult.rows.map(row => row.column_name);
|
|
39
|
+
|
|
40
|
+
// If primary key is already (id, tenant_id), nothing to do
|
|
41
|
+
if (pkColumns.includes('tenant_id') && pkColumns.includes('id') && pkColumns.length === 2) {
|
|
42
|
+
console.log('Primary key already correct (id, tenant_id)');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// If primary key is (id, assistant_id), we need to change it
|
|
47
|
+
if (pkColumns.includes('id') && pkColumns.includes('assistant_id')) {
|
|
48
|
+
console.log('Changing primary key from (id, assistant_id) to (id, tenant_id)');
|
|
49
|
+
|
|
50
|
+
// Check if tenant_id column exists
|
|
51
|
+
const tenantIdExists = await client.query(`
|
|
52
|
+
SELECT EXISTS (
|
|
53
|
+
SELECT FROM information_schema.columns
|
|
54
|
+
WHERE table_name = 'lattice_threads'
|
|
55
|
+
AND column_name = 'tenant_id'
|
|
56
|
+
)
|
|
57
|
+
`);
|
|
58
|
+
|
|
59
|
+
if (!tenantIdExists.rows[0].exists) {
|
|
60
|
+
// Add tenant_id column if it doesn't exist
|
|
61
|
+
await client.query(`
|
|
62
|
+
ALTER TABLE lattice_threads
|
|
63
|
+
ADD COLUMN tenant_id VARCHAR(255) DEFAULT 'default'
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Update existing records to have tenant_id
|
|
68
|
+
await client.query(`
|
|
69
|
+
UPDATE lattice_threads
|
|
70
|
+
SET tenant_id = 'default'
|
|
71
|
+
WHERE tenant_id IS NULL
|
|
72
|
+
`);
|
|
73
|
+
|
|
74
|
+
// Make tenant_id NOT NULL
|
|
75
|
+
await client.query(`
|
|
76
|
+
ALTER TABLE lattice_threads
|
|
77
|
+
ALTER COLUMN tenant_id SET NOT NULL
|
|
78
|
+
`);
|
|
79
|
+
|
|
80
|
+
// Drop the old primary key
|
|
81
|
+
await client.query(`
|
|
82
|
+
ALTER TABLE lattice_threads
|
|
83
|
+
DROP CONSTRAINT IF EXISTS lattice_threads_pkey
|
|
84
|
+
`);
|
|
85
|
+
|
|
86
|
+
// Add new composite primary key
|
|
87
|
+
await client.query(`
|
|
88
|
+
ALTER TABLE lattice_threads
|
|
89
|
+
ADD PRIMARY KEY (id, tenant_id)
|
|
90
|
+
`);
|
|
91
|
+
|
|
92
|
+
console.log('Primary key changed successfully to (id, tenant_id)');
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
down: async (client: PoolClient) => {
|
|
96
|
+
// Reverting this is complex and potentially dangerous
|
|
97
|
+
console.warn('Down migration for changeThreadPrimaryKey is not supported');
|
|
98
|
+
},
|
|
99
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thread table tenant isolation migration
|
|
3
|
+
* Adds tenant_id column to lattice_threads table
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PoolClient } from "pg";
|
|
7
|
+
import { Migration } from "./migration";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Migration: Add tenant_id column to threads table
|
|
11
|
+
*/
|
|
12
|
+
export const addThreadTenantId: Migration = {
|
|
13
|
+
version: 15,
|
|
14
|
+
name: "add_thread_tenant_id",
|
|
15
|
+
up: async (client: PoolClient) => {
|
|
16
|
+
// Add tenant_id column
|
|
17
|
+
await client.query(`
|
|
18
|
+
ALTER TABLE lattice_threads
|
|
19
|
+
ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(255) DEFAULT 'default'
|
|
20
|
+
`);
|
|
21
|
+
|
|
22
|
+
// Update existing records to have tenant_id
|
|
23
|
+
await client.query(`
|
|
24
|
+
UPDATE lattice_threads
|
|
25
|
+
SET tenant_id = 'default'
|
|
26
|
+
WHERE tenant_id IS NULL
|
|
27
|
+
`);
|
|
28
|
+
|
|
29
|
+
// Make tenant_id NOT NULL after setting defaults
|
|
30
|
+
await client.query(`
|
|
31
|
+
ALTER TABLE lattice_threads
|
|
32
|
+
ALTER COLUMN tenant_id SET NOT NULL
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
// Drop the default constraint
|
|
36
|
+
await client.query(`
|
|
37
|
+
ALTER TABLE lattice_threads
|
|
38
|
+
ALTER COLUMN tenant_id DROP DEFAULT
|
|
39
|
+
`);
|
|
40
|
+
|
|
41
|
+
// Create index for tenant-based queries
|
|
42
|
+
await client.query(`
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_threads_tenant_id
|
|
44
|
+
ON lattice_threads(tenant_id)
|
|
45
|
+
`);
|
|
46
|
+
},
|
|
47
|
+
down: async (client: PoolClient) => {
|
|
48
|
+
await client.query(
|
|
49
|
+
"DROP INDEX IF EXISTS idx_lattice_threads_tenant_id"
|
|
50
|
+
);
|
|
51
|
+
await client.query(
|
|
52
|
+
"ALTER TABLE lattice_threads DROP COLUMN IF EXISTS tenant_id"
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
};
|