@mastra/pg 0.10.4-alpha.0 → 0.11.0-alpha.2

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.
@@ -1,23 +1,23 @@
1
1
 
2
- > @mastra/pg@0.10.4-alpha.0 build /home/runner/work/mastra/mastra/stores/pg
2
+ > @mastra/pg@0.11.0-alpha.2 build /home/runner/work/mastra/mastra/stores/pg
3
3
  > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.5.0
8
8
  TSC Build start
9
- TSC ⚡️ Build success in 11001ms
9
+ TSC ⚡️ Build success in 10857ms
10
10
  DTS Build start
11
11
  CLI Target: es2022
12
12
  Analysis will use the bundled TypeScript version 5.8.3
13
13
  Writing package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.ts
14
14
  Analysis will use the bundled TypeScript version 5.8.3
15
15
  Writing package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.cts
16
- DTS ⚡️ Build success in 11284ms
16
+ DTS ⚡️ Build success in 13576ms
17
17
  CLI Cleaning output folder
18
18
  ESM Build start
19
19
  CJS Build start
20
- ESM dist/index.js 67.19 KB
21
- ESM ⚡️ Build success in 1382ms
22
- CJS dist/index.cjs 67.75 KB
23
- CJS ⚡️ Build success in 1382ms
20
+ CJS dist/index.cjs 68.46 KB
21
+ CJS ⚡️ Build success in 1747ms
22
+ ESM dist/index.js 67.89 KB
23
+ ESM ⚡️ Build success in 1749ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @mastra/pg
2
2
 
3
+ ## 0.11.0-alpha.2
4
+
5
+ ### Patch Changes
6
+
7
+ - eed55d7: quotes table and schema names for vector and storage to allow for camelcase
8
+ - Updated dependencies [d70c420]
9
+ - Updated dependencies [2a16996]
10
+ - @mastra/core@0.10.6-alpha.3
11
+
12
+ ## 0.11.0-alpha.1
13
+
14
+ ### Minor Changes
15
+
16
+ - 704d1ca: Thread Timestamp Auto-Update Enhancement
17
+ Added automatic thread updatedAt timestamp updates when messages are saved across all storage providers
18
+ Enhanced user experience: Threads now accurately reflect their latest activity with automatic timestamp updates when new messages are added
19
+ Universal implementation: Consistent behavior across all 7 storage backends (ClickHouse, Cloudflare D1, DynamoDB, MongoDB, PostgreSQL, Upstash, LibSQL)
20
+ Performance optimized: Updates execute in parallel with message saving operations for minimal performance impact
21
+ Backwards compatible: No breaking changes - existing code continues to work unchanged
22
+ Improved conversation ordering: Chat interfaces can now properly sort threads by actual last activity
23
+ This enhancement resolves the issue where active conversations appeared stale due to outdated thread timestamps, providing better conversation management and user experience in chat applications.
24
+
3
25
  ## 0.10.4-alpha.0
4
26
 
5
27
  ### Patch Changes
@@ -196,6 +196,7 @@ declare class PgVector extends MastraVector {
196
196
  });
197
197
  private getMutexByName;
198
198
  private getTableName;
199
+ private getSchemaName;
199
200
  transformFilter(filter?: VectorFilter): VectorFilter;
200
201
  getIndexInfo({ indexName }: DescribeIndexParams): Promise<PGIndexStats>;
201
202
  query({ indexName, queryVector, topK, filter, includeVector, minScore, ef, probes, }: PgQueryVectorParams): Promise<QueryResult[]>;
@@ -276,6 +277,7 @@ declare class PostgresStore extends MastraStorage {
276
277
  selectByIncludeResourceScope: boolean;
277
278
  };
278
279
  private getTableName;
280
+ private getSchemaName;
279
281
  /** @deprecated use getEvals instead */
280
282
  getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]>;
281
283
  private transformEvalRow;
@@ -196,6 +196,7 @@ declare class PgVector extends MastraVector {
196
196
  });
197
197
  private getMutexByName;
198
198
  private getTableName;
199
+ private getSchemaName;
199
200
  transformFilter(filter?: VectorFilter): VectorFilter;
200
201
  getIndexInfo({ indexName }: DescribeIndexParams): Promise<PGIndexStats>;
201
202
  query({ indexName, queryVector, topK, filter, includeVector, minScore, ef, probes, }: PgQueryVectorParams): Promise<QueryResult[]>;
@@ -276,6 +277,7 @@ declare class PostgresStore extends MastraStorage {
276
277
  selectByIncludeResourceScope: boolean;
277
278
  };
278
279
  private getTableName;
280
+ private getSchemaName;
279
281
  /** @deprecated use getEvals instead */
280
282
  getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]>;
281
283
  private transformEvalRow;
package/dist/index.cjs CHANGED
@@ -420,8 +420,16 @@ var PgVector = class extends vector.MastraVector {
420
420
  }
421
421
  getTableName(indexName) {
422
422
  const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
423
- const parsedSchemaName = this.schema ? utils.parseSqlIdentifier(this.schema, "schema name") : void 0;
424
- return parsedSchemaName ? `${parsedSchemaName}.${parsedIndexName}` : parsedIndexName;
423
+ const quotedIndexName = `"${parsedIndexName}"`;
424
+ const quotedSchemaName = this.getSchemaName();
425
+ const quotedVectorName = `"${parsedIndexName}_vector_idx"`;
426
+ return {
427
+ tableName: quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName,
428
+ vectorIndexName: quotedVectorName
429
+ };
430
+ }
431
+ getSchemaName() {
432
+ return this.schema ? `"${utils.parseSqlIdentifier(this.schema, "schema name")}"` : void 0;
425
433
  }
426
434
  transformFilter(filter) {
427
435
  const translator = new PGFilterTranslator();
@@ -463,7 +471,7 @@ var PgVector = class extends vector.MastraVector {
463
471
  if (indexInfo.type === "ivfflat" && probes) {
464
472
  await client.query(`SET LOCAL ivfflat.probes = ${probes}`);
465
473
  }
466
- const tableName = this.getTableName(indexName);
474
+ const { tableName } = this.getTableName(indexName);
467
475
  const query = `
468
476
  WITH vector_scores AS (
469
477
  SELECT
@@ -491,7 +499,7 @@ var PgVector = class extends vector.MastraVector {
491
499
  }
492
500
  }
493
501
  async upsert({ indexName, vectors, metadata, ids }) {
494
- const tableName = this.getTableName(indexName);
502
+ const { tableName } = this.getTableName(indexName);
495
503
  const client = await this.pool.connect();
496
504
  try {
497
505
  await client.query("BEGIN");
@@ -559,7 +567,7 @@ var PgVector = class extends vector.MastraVector {
559
567
  const schemaExists = schemaCheck.rows[0].exists;
560
568
  if (!schemaExists) {
561
569
  try {
562
- await client.query(`CREATE SCHEMA IF NOT EXISTS ${this.schema}`);
570
+ await client.query(`CREATE SCHEMA IF NOT EXISTS ${this.getSchemaName()}`);
563
571
  this.logger.info(`Schema "${this.schema}" created successfully`);
564
572
  } catch (error) {
565
573
  this.logger.error(`Failed to create schema "${this.schema}"`, { error });
@@ -588,7 +596,7 @@ var PgVector = class extends vector.MastraVector {
588
596
  indexConfig = {},
589
597
  buildIndex = true
590
598
  }) {
591
- const tableName = this.getTableName(indexName);
599
+ const { tableName } = this.getTableName(indexName);
592
600
  if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
593
601
  throw new Error("Invalid index name format");
594
602
  }
@@ -639,9 +647,9 @@ var PgVector = class extends vector.MastraVector {
639
647
  async setupIndex({ indexName, metric, indexConfig }, client) {
640
648
  const mutex = this.getMutexByName(`build-${indexName}`);
641
649
  await mutex.runExclusive(async () => {
642
- const tableName = this.getTableName(indexName);
650
+ const { tableName, vectorIndexName } = this.getTableName(indexName);
643
651
  if (this.createdIndexes.has(indexName)) {
644
- await client.query(`DROP INDEX IF EXISTS ${tableName}_vector_idx`);
652
+ await client.query(`DROP INDEX IF EXISTS ${vectorIndexName}`);
645
653
  }
646
654
  if (indexConfig.type === "flat") {
647
655
  this.describeIndexCache.delete(indexName);
@@ -653,7 +661,7 @@ var PgVector = class extends vector.MastraVector {
653
661
  const m = indexConfig.hnsw?.m ?? 8;
654
662
  const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
655
663
  indexSQL = `
656
- CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
664
+ CREATE INDEX IF NOT EXISTS ${vectorIndexName}
657
665
  ON ${tableName}
658
666
  USING hnsw (embedding ${metricOp})
659
667
  WITH (
@@ -670,7 +678,7 @@ var PgVector = class extends vector.MastraVector {
670
678
  lists = Math.max(100, Math.min(4e3, Math.floor(Math.sqrt(size) * 2)));
671
679
  }
672
680
  indexSQL = `
673
- CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
681
+ CREATE INDEX IF NOT EXISTS ${vectorIndexName}
674
682
  ON ${tableName}
675
683
  USING ivfflat (embedding ${metricOp})
676
684
  WITH (lists = ${lists});
@@ -741,7 +749,7 @@ var PgVector = class extends vector.MastraVector {
741
749
  async describeIndex({ indexName }) {
742
750
  const client = await this.pool.connect();
743
751
  try {
744
- const tableName = this.getTableName(indexName);
752
+ const { tableName } = this.getTableName(indexName);
745
753
  const tableExistsQuery = `
746
754
  SELECT 1
747
755
  FROM information_schema.columns
@@ -815,7 +823,7 @@ var PgVector = class extends vector.MastraVector {
815
823
  async deleteIndex({ indexName }) {
816
824
  const client = await this.pool.connect();
817
825
  try {
818
- const tableName = this.getTableName(indexName);
826
+ const { tableName } = this.getTableName(indexName);
819
827
  await client.query(`DROP TABLE IF EXISTS ${tableName} CASCADE`);
820
828
  this.createdIndexes.delete(indexName);
821
829
  } catch (error) {
@@ -828,7 +836,7 @@ var PgVector = class extends vector.MastraVector {
828
836
  async truncateIndex({ indexName }) {
829
837
  const client = await this.pool.connect();
830
838
  try {
831
- const tableName = this.getTableName(indexName);
839
+ const { tableName } = this.getTableName(indexName);
832
840
  await client.query(`TRUNCATE ${tableName}`);
833
841
  } catch (e) {
834
842
  await client.query("ROLLBACK");
@@ -871,7 +879,7 @@ var PgVector = class extends vector.MastraVector {
871
879
  if (updateParts.length === 0) {
872
880
  return;
873
881
  }
874
- const tableName = this.getTableName(indexName);
882
+ const { tableName } = this.getTableName(indexName);
875
883
  const query = `
876
884
  UPDATE ${tableName}
877
885
  SET ${updateParts.join(", ")}
@@ -894,7 +902,7 @@ var PgVector = class extends vector.MastraVector {
894
902
  async deleteVector({ indexName, id }) {
895
903
  const client = await this.pool.connect();
896
904
  try {
897
- const tableName = this.getTableName(indexName);
905
+ const { tableName } = this.getTableName(indexName);
898
906
  const query = `
899
907
  DELETE FROM ${tableName}
900
908
  WHERE vector_id = $1
@@ -950,9 +958,13 @@ var PostgresStore = class extends storage.MastraStorage {
950
958
  };
951
959
  }
952
960
  getTableName(indexName) {
953
- const parsedIndexName = utils.parseSqlIdentifier(indexName, "table name");
954
- const parsedSchemaName = this.schema ? utils.parseSqlIdentifier(this.schema, "schema name") : void 0;
955
- return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
961
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
962
+ const quotedIndexName = `"${parsedIndexName}"`;
963
+ const quotedSchemaName = this.getSchemaName();
964
+ return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
965
+ }
966
+ getSchemaName() {
967
+ return this.schema ? `"${utils.parseSqlIdentifier(this.schema, "schema name")}"` : void 0;
956
968
  }
957
969
  /** @deprecated use getEvals instead */
958
970
  async getEvalsByAgentName(agentName, type) {
@@ -1117,7 +1129,7 @@ var PostgresStore = class extends storage.MastraStorage {
1117
1129
  );
1118
1130
  if (!schemaExists?.exists) {
1119
1131
  try {
1120
- await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${this.schema}`);
1132
+ await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${this.getSchemaName()}`);
1121
1133
  this.logger.info(`Schema "${this.schema}" created successfully`);
1122
1134
  } catch (error) {
1123
1135
  this.logger.error(`Failed to create schema "${this.schema}"`, { error });
@@ -1594,7 +1606,7 @@ var PostgresStore = class extends storage.MastraStorage {
1594
1606
  throw new Error(`Thread ${threadId} not found`);
1595
1607
  }
1596
1608
  await this.db.tx(async (t) => {
1597
- for (const message of messages) {
1609
+ const messageInserts = messages.map((message) => {
1598
1610
  if (!message.threadId) {
1599
1611
  throw new Error(
1600
1612
  `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
@@ -1605,7 +1617,7 @@ var PostgresStore = class extends storage.MastraStorage {
1605
1617
  `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
1606
1618
  );
1607
1619
  }
1608
- await t.none(
1620
+ return t.none(
1609
1621
  `INSERT INTO ${this.getTableName(storage.TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
1610
1622
  VALUES ($1, $2, $3, $4, $5, $6, $7)`,
1611
1623
  [
@@ -1618,7 +1630,14 @@ var PostgresStore = class extends storage.MastraStorage {
1618
1630
  message.resourceId
1619
1631
  ]
1620
1632
  );
1621
- }
1633
+ });
1634
+ const threadUpdate = t.none(
1635
+ `UPDATE ${this.getTableName(storage.TABLE_THREADS)}
1636
+ SET "updatedAt" = $1
1637
+ WHERE id = $2`,
1638
+ [(/* @__PURE__ */ new Date()).toISOString(), threadId]
1639
+ );
1640
+ await Promise.all([...messageInserts, threadUpdate]);
1622
1641
  });
1623
1642
  const list = new agent.MessageList().add(messages, "memory");
1624
1643
  if (format === `v2`) return list.get.all.v2();
package/dist/index.js CHANGED
@@ -412,8 +412,16 @@ var PgVector = class extends MastraVector {
412
412
  }
413
413
  getTableName(indexName) {
414
414
  const parsedIndexName = parseSqlIdentifier(indexName, "index name");
415
- const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, "schema name") : void 0;
416
- return parsedSchemaName ? `${parsedSchemaName}.${parsedIndexName}` : parsedIndexName;
415
+ const quotedIndexName = `"${parsedIndexName}"`;
416
+ const quotedSchemaName = this.getSchemaName();
417
+ const quotedVectorName = `"${parsedIndexName}_vector_idx"`;
418
+ return {
419
+ tableName: quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName,
420
+ vectorIndexName: quotedVectorName
421
+ };
422
+ }
423
+ getSchemaName() {
424
+ return this.schema ? `"${parseSqlIdentifier(this.schema, "schema name")}"` : void 0;
417
425
  }
418
426
  transformFilter(filter) {
419
427
  const translator = new PGFilterTranslator();
@@ -455,7 +463,7 @@ var PgVector = class extends MastraVector {
455
463
  if (indexInfo.type === "ivfflat" && probes) {
456
464
  await client.query(`SET LOCAL ivfflat.probes = ${probes}`);
457
465
  }
458
- const tableName = this.getTableName(indexName);
466
+ const { tableName } = this.getTableName(indexName);
459
467
  const query = `
460
468
  WITH vector_scores AS (
461
469
  SELECT
@@ -483,7 +491,7 @@ var PgVector = class extends MastraVector {
483
491
  }
484
492
  }
485
493
  async upsert({ indexName, vectors, metadata, ids }) {
486
- const tableName = this.getTableName(indexName);
494
+ const { tableName } = this.getTableName(indexName);
487
495
  const client = await this.pool.connect();
488
496
  try {
489
497
  await client.query("BEGIN");
@@ -551,7 +559,7 @@ var PgVector = class extends MastraVector {
551
559
  const schemaExists = schemaCheck.rows[0].exists;
552
560
  if (!schemaExists) {
553
561
  try {
554
- await client.query(`CREATE SCHEMA IF NOT EXISTS ${this.schema}`);
562
+ await client.query(`CREATE SCHEMA IF NOT EXISTS ${this.getSchemaName()}`);
555
563
  this.logger.info(`Schema "${this.schema}" created successfully`);
556
564
  } catch (error) {
557
565
  this.logger.error(`Failed to create schema "${this.schema}"`, { error });
@@ -580,7 +588,7 @@ var PgVector = class extends MastraVector {
580
588
  indexConfig = {},
581
589
  buildIndex = true
582
590
  }) {
583
- const tableName = this.getTableName(indexName);
591
+ const { tableName } = this.getTableName(indexName);
584
592
  if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
585
593
  throw new Error("Invalid index name format");
586
594
  }
@@ -631,9 +639,9 @@ var PgVector = class extends MastraVector {
631
639
  async setupIndex({ indexName, metric, indexConfig }, client) {
632
640
  const mutex = this.getMutexByName(`build-${indexName}`);
633
641
  await mutex.runExclusive(async () => {
634
- const tableName = this.getTableName(indexName);
642
+ const { tableName, vectorIndexName } = this.getTableName(indexName);
635
643
  if (this.createdIndexes.has(indexName)) {
636
- await client.query(`DROP INDEX IF EXISTS ${tableName}_vector_idx`);
644
+ await client.query(`DROP INDEX IF EXISTS ${vectorIndexName}`);
637
645
  }
638
646
  if (indexConfig.type === "flat") {
639
647
  this.describeIndexCache.delete(indexName);
@@ -645,7 +653,7 @@ var PgVector = class extends MastraVector {
645
653
  const m = indexConfig.hnsw?.m ?? 8;
646
654
  const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
647
655
  indexSQL = `
648
- CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
656
+ CREATE INDEX IF NOT EXISTS ${vectorIndexName}
649
657
  ON ${tableName}
650
658
  USING hnsw (embedding ${metricOp})
651
659
  WITH (
@@ -662,7 +670,7 @@ var PgVector = class extends MastraVector {
662
670
  lists = Math.max(100, Math.min(4e3, Math.floor(Math.sqrt(size) * 2)));
663
671
  }
664
672
  indexSQL = `
665
- CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
673
+ CREATE INDEX IF NOT EXISTS ${vectorIndexName}
666
674
  ON ${tableName}
667
675
  USING ivfflat (embedding ${metricOp})
668
676
  WITH (lists = ${lists});
@@ -733,7 +741,7 @@ var PgVector = class extends MastraVector {
733
741
  async describeIndex({ indexName }) {
734
742
  const client = await this.pool.connect();
735
743
  try {
736
- const tableName = this.getTableName(indexName);
744
+ const { tableName } = this.getTableName(indexName);
737
745
  const tableExistsQuery = `
738
746
  SELECT 1
739
747
  FROM information_schema.columns
@@ -807,7 +815,7 @@ var PgVector = class extends MastraVector {
807
815
  async deleteIndex({ indexName }) {
808
816
  const client = await this.pool.connect();
809
817
  try {
810
- const tableName = this.getTableName(indexName);
818
+ const { tableName } = this.getTableName(indexName);
811
819
  await client.query(`DROP TABLE IF EXISTS ${tableName} CASCADE`);
812
820
  this.createdIndexes.delete(indexName);
813
821
  } catch (error) {
@@ -820,7 +828,7 @@ var PgVector = class extends MastraVector {
820
828
  async truncateIndex({ indexName }) {
821
829
  const client = await this.pool.connect();
822
830
  try {
823
- const tableName = this.getTableName(indexName);
831
+ const { tableName } = this.getTableName(indexName);
824
832
  await client.query(`TRUNCATE ${tableName}`);
825
833
  } catch (e) {
826
834
  await client.query("ROLLBACK");
@@ -863,7 +871,7 @@ var PgVector = class extends MastraVector {
863
871
  if (updateParts.length === 0) {
864
872
  return;
865
873
  }
866
- const tableName = this.getTableName(indexName);
874
+ const { tableName } = this.getTableName(indexName);
867
875
  const query = `
868
876
  UPDATE ${tableName}
869
877
  SET ${updateParts.join(", ")}
@@ -886,7 +894,7 @@ var PgVector = class extends MastraVector {
886
894
  async deleteVector({ indexName, id }) {
887
895
  const client = await this.pool.connect();
888
896
  try {
889
- const tableName = this.getTableName(indexName);
897
+ const { tableName } = this.getTableName(indexName);
890
898
  const query = `
891
899
  DELETE FROM ${tableName}
892
900
  WHERE vector_id = $1
@@ -942,9 +950,13 @@ var PostgresStore = class extends MastraStorage {
942
950
  };
943
951
  }
944
952
  getTableName(indexName) {
945
- const parsedIndexName = parseSqlIdentifier(indexName, "table name");
946
- const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, "schema name") : void 0;
947
- return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
953
+ const parsedIndexName = parseSqlIdentifier(indexName, "index name");
954
+ const quotedIndexName = `"${parsedIndexName}"`;
955
+ const quotedSchemaName = this.getSchemaName();
956
+ return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
957
+ }
958
+ getSchemaName() {
959
+ return this.schema ? `"${parseSqlIdentifier(this.schema, "schema name")}"` : void 0;
948
960
  }
949
961
  /** @deprecated use getEvals instead */
950
962
  async getEvalsByAgentName(agentName, type) {
@@ -1109,7 +1121,7 @@ var PostgresStore = class extends MastraStorage {
1109
1121
  );
1110
1122
  if (!schemaExists?.exists) {
1111
1123
  try {
1112
- await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${this.schema}`);
1124
+ await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${this.getSchemaName()}`);
1113
1125
  this.logger.info(`Schema "${this.schema}" created successfully`);
1114
1126
  } catch (error) {
1115
1127
  this.logger.error(`Failed to create schema "${this.schema}"`, { error });
@@ -1586,7 +1598,7 @@ var PostgresStore = class extends MastraStorage {
1586
1598
  throw new Error(`Thread ${threadId} not found`);
1587
1599
  }
1588
1600
  await this.db.tx(async (t) => {
1589
- for (const message of messages) {
1601
+ const messageInserts = messages.map((message) => {
1590
1602
  if (!message.threadId) {
1591
1603
  throw new Error(
1592
1604
  `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
@@ -1597,7 +1609,7 @@ var PostgresStore = class extends MastraStorage {
1597
1609
  `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
1598
1610
  );
1599
1611
  }
1600
- await t.none(
1612
+ return t.none(
1601
1613
  `INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
1602
1614
  VALUES ($1, $2, $3, $4, $5, $6, $7)`,
1603
1615
  [
@@ -1610,7 +1622,14 @@ var PostgresStore = class extends MastraStorage {
1610
1622
  message.resourceId
1611
1623
  ]
1612
1624
  );
1613
- }
1625
+ });
1626
+ const threadUpdate = t.none(
1627
+ `UPDATE ${this.getTableName(TABLE_THREADS)}
1628
+ SET "updatedAt" = $1
1629
+ WHERE id = $2`,
1630
+ [(/* @__PURE__ */ new Date()).toISOString(), threadId]
1631
+ );
1632
+ await Promise.all([...messageInserts, threadUpdate]);
1614
1633
  });
1615
1634
  const list = new MessageList().add(messages, "memory");
1616
1635
  if (format === `v2`) return list.get.all.v2();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/pg",
3
- "version": "0.10.4-alpha.0",
3
+ "version": "0.11.0-alpha.2",
4
4
  "description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -34,8 +34,8 @@
34
34
  "typescript": "^5.8.3",
35
35
  "vitest": "^3.2.3",
36
36
  "@internal/lint": "0.0.12",
37
- "@mastra/core": "0.10.6-alpha.0",
38
- "@internal/storage-test-utils": "0.0.8"
37
+ "@internal/storage-test-utils": "0.0.8",
38
+ "@mastra/core": "0.10.6-alpha.3"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@mastra/core": ">=0.10.4-0 <0.11.0"
@@ -173,6 +173,28 @@ describe('PostgresStore', () => {
173
173
  const retrievedMessages = await store.getMessages({ threadId: thread.id });
174
174
  expect(retrievedMessages).toHaveLength(0);
175
175
  });
176
+
177
+ it('should update thread updatedAt when a message is saved to it', async () => {
178
+ const thread = createSampleThread();
179
+ await store.saveThread({ thread });
180
+
181
+ // Get the initial thread to capture the original updatedAt
182
+ const initialThread = await store.getThreadById({ threadId: thread.id });
183
+ expect(initialThread).toBeDefined();
184
+ const originalUpdatedAt = initialThread!.updatedAt;
185
+
186
+ // Wait a small amount to ensure different timestamp
187
+ await new Promise(resolve => setTimeout(resolve, 10));
188
+
189
+ // Create and save a message to the thread
190
+ const message = createSampleMessageV1({ threadId: thread.id });
191
+ await store.saveMessages({ messages: [message] });
192
+
193
+ // Retrieve the thread again and check that updatedAt was updated
194
+ const updatedThread = await store.getThreadById({ threadId: thread.id });
195
+ expect(updatedThread).toBeDefined();
196
+ expect(updatedThread!.updatedAt.getTime()).toBeGreaterThan(originalUpdatedAt.getTime());
197
+ });
176
198
  });
177
199
 
178
200
  describe('Message Operations', () => {
@@ -994,7 +1016,7 @@ describe('PostgresStore', () => {
994
1016
  });
995
1017
 
996
1018
  describe('Schema Support', () => {
997
- const customSchema = 'mastra_test';
1019
+ const customSchema = 'mastraTest';
998
1020
  let customSchemaStore: PostgresStore;
999
1021
 
1000
1022
  beforeAll(async () => {
@@ -1472,10 +1494,83 @@ describe('PostgresStore', () => {
1472
1494
  });
1473
1495
  });
1474
1496
 
1497
+ describe('PgStorage Table Name Quoting', () => {
1498
+ const camelCaseTable = 'TestCamelCaseTable';
1499
+ const snakeCaseTable = 'test_snake_case_table';
1500
+ const BASE_SCHEMA = {
1501
+ id: { type: 'integer', primaryKey: true, nullable: false },
1502
+ name: { type: 'text', nullable: true },
1503
+ } as Record<string, StorageColumn>;
1504
+
1505
+ beforeEach(async () => {
1506
+ // Only clear tables if store is initialized
1507
+ try {
1508
+ // Clear tables before each test
1509
+ await store.clearTable({ tableName: camelCaseTable as TABLE_NAMES });
1510
+ await store.clearTable({ tableName: snakeCaseTable as TABLE_NAMES });
1511
+ } catch (error) {
1512
+ // Ignore errors during table clearing
1513
+ console.warn('Error clearing tables:', error);
1514
+ }
1515
+ });
1516
+
1517
+ afterEach(async () => {
1518
+ // Only clear tables if store is initialized
1519
+ try {
1520
+ // Clear tables before each test
1521
+ await store.clearTable({ tableName: camelCaseTable as TABLE_NAMES });
1522
+ await store.clearTable({ tableName: snakeCaseTable as TABLE_NAMES });
1523
+ } catch (error) {
1524
+ // Ignore errors during table clearing
1525
+ console.warn('Error clearing tables:', error);
1526
+ }
1527
+ });
1528
+
1529
+ it('should create and upsert to a camelCase table without quoting errors', async () => {
1530
+ await expect(
1531
+ store.createTable({
1532
+ tableName: camelCaseTable as TABLE_NAMES,
1533
+ schema: BASE_SCHEMA,
1534
+ }),
1535
+ ).resolves.not.toThrow();
1536
+
1537
+ await store.insert({
1538
+ tableName: camelCaseTable as TABLE_NAMES,
1539
+ record: { id: '1', name: 'Alice' },
1540
+ });
1541
+
1542
+ const row = await store.load({
1543
+ tableName: camelCaseTable as TABLE_NAMES,
1544
+ keys: { id: '1' },
1545
+ });
1546
+ expect(row?.name).toBe('Alice');
1547
+ });
1548
+
1549
+ it('should create and upsert to a snake_case table without quoting errors', async () => {
1550
+ await expect(
1551
+ store.createTable({
1552
+ tableName: snakeCaseTable as TABLE_NAMES,
1553
+ schema: BASE_SCHEMA,
1554
+ }),
1555
+ ).resolves.not.toThrow();
1556
+
1557
+ await store.insert({
1558
+ tableName: snakeCaseTable as TABLE_NAMES,
1559
+ record: { id: '2', name: 'Bob' },
1560
+ });
1561
+
1562
+ const row = await store.load({
1563
+ tableName: snakeCaseTable as TABLE_NAMES,
1564
+ keys: { id: '2' },
1565
+ });
1566
+ expect(row?.name).toBe('Bob');
1567
+ });
1568
+ });
1569
+
1475
1570
  describe('Permission Handling', () => {
1476
1571
  const schemaRestrictedUser = 'mastra_schema_restricted_storage';
1477
1572
  const restrictedPassword = 'test123';
1478
- const testSchema = 'test_schema';
1573
+ const testSchema = 'testSchema';
1479
1574
  let adminDb: pgPromise.IDatabase<{}>;
1480
1575
  let pgpAdmin: pgPromise.IMain;
1481
1576
 
@@ -96,9 +96,14 @@ export class PostgresStore extends MastraStorage {
96
96
  }
97
97
 
98
98
  private getTableName(indexName: string) {
99
- const parsedIndexName = parseSqlIdentifier(indexName, 'table name');
100
- const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, 'schema name') : undefined;
101
- return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
99
+ const parsedIndexName = parseSqlIdentifier(indexName, 'index name');
100
+ const quotedIndexName = `"${parsedIndexName}"`;
101
+ const quotedSchemaName = this.getSchemaName();
102
+ return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
103
+ }
104
+
105
+ private getSchemaName() {
106
+ return this.schema ? `"${parseSqlIdentifier(this.schema, 'schema name')}"` : undefined;
102
107
  }
103
108
 
104
109
  /** @deprecated use getEvals instead */
@@ -311,7 +316,7 @@ export class PostgresStore extends MastraStorage {
311
316
 
312
317
  if (!schemaExists?.exists) {
313
318
  try {
314
- await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${this.schema}`);
319
+ await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${this.getSchemaName()}`);
315
320
  this.logger.info(`Schema "${this.schema}" created successfully`);
316
321
  } catch (error) {
317
322
  this.logger.error(`Failed to create schema "${this.schema}"`, { error });
@@ -904,7 +909,8 @@ export class PostgresStore extends MastraStorage {
904
909
  }
905
910
 
906
911
  await this.db.tx(async t => {
907
- for (const message of messages) {
912
+ // Execute message inserts and thread update in parallel for better performance
913
+ const messageInserts = messages.map(message => {
908
914
  if (!message.threadId) {
909
915
  throw new Error(
910
916
  `Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`,
@@ -915,7 +921,7 @@ export class PostgresStore extends MastraStorage {
915
921
  `Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`,
916
922
  );
917
923
  }
918
- await t.none(
924
+ return t.none(
919
925
  `INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
920
926
  VALUES ($1, $2, $3, $4, $5, $6, $7)`,
921
927
  [
@@ -928,7 +934,16 @@ export class PostgresStore extends MastraStorage {
928
934
  message.resourceId,
929
935
  ],
930
936
  );
931
- }
937
+ });
938
+
939
+ const threadUpdate = t.none(
940
+ `UPDATE ${this.getTableName(TABLE_THREADS)}
941
+ SET "updatedAt" = $1
942
+ WHERE id = $2`,
943
+ [new Date().toISOString(), threadId],
944
+ );
945
+
946
+ await Promise.all([...messageInserts, threadUpdate]);
932
947
  });
933
948
 
934
949
  const list = new MessageList().add(messages, 'memory');
@@ -1647,6 +1647,75 @@ describe('PgVector', () => {
1647
1647
  });
1648
1648
  });
1649
1649
 
1650
+ describe('PgVector Table Name Quoting', () => {
1651
+ const camelCaseIndex = 'TestCamelCaseIndex';
1652
+ const snakeCaseIndex = 'test_snake_case_index';
1653
+
1654
+ beforeEach(async () => {
1655
+ // Clean up any existing indexes
1656
+ try {
1657
+ await vectorDB.deleteIndex({ indexName: camelCaseIndex });
1658
+ } catch {
1659
+ // Ignore if doesn't exist
1660
+ }
1661
+ try {
1662
+ await vectorDB.deleteIndex({ indexName: snakeCaseIndex });
1663
+ } catch {
1664
+ // Ignore if doesn't exist
1665
+ }
1666
+ });
1667
+
1668
+ afterEach(async () => {
1669
+ // Clean up indexes after each test
1670
+ try {
1671
+ await vectorDB.deleteIndex({ indexName: camelCaseIndex });
1672
+ } catch {
1673
+ // Ignore if doesn't exist
1674
+ }
1675
+ try {
1676
+ await vectorDB.deleteIndex({ indexName: snakeCaseIndex });
1677
+ } catch {
1678
+ // Ignore if doesn't exist
1679
+ }
1680
+ });
1681
+
1682
+ it('should create and query a camelCase index without quoting errors', async () => {
1683
+ await expect(
1684
+ vectorDB.createIndex({
1685
+ indexName: camelCaseIndex,
1686
+ dimension: 3,
1687
+ metric: 'cosine',
1688
+ indexConfig: { type: 'hnsw' },
1689
+ }),
1690
+ ).resolves.not.toThrow();
1691
+
1692
+ const results = await vectorDB.query({
1693
+ indexName: camelCaseIndex,
1694
+ queryVector: [1, 0, 0],
1695
+ topK: 1,
1696
+ });
1697
+ expect(Array.isArray(results)).toBe(true);
1698
+ });
1699
+
1700
+ it('should create and query a snake_case index without quoting errors', async () => {
1701
+ await expect(
1702
+ vectorDB.createIndex({
1703
+ indexName: snakeCaseIndex,
1704
+ dimension: 3,
1705
+ metric: 'cosine',
1706
+ indexConfig: { type: 'hnsw' },
1707
+ }),
1708
+ ).resolves.not.toThrow();
1709
+
1710
+ const results = await vectorDB.query({
1711
+ indexName: snakeCaseIndex,
1712
+ queryVector: [1, 0, 0],
1713
+ topK: 1,
1714
+ });
1715
+ expect(Array.isArray(results)).toBe(true);
1716
+ });
1717
+ });
1718
+
1650
1719
  // Regex Operator Tests
1651
1720
  describe('Regex Operators', () => {
1652
1721
  it('should handle $regex with case sensitivity', async () => {
@@ -1878,7 +1947,7 @@ describe('PgVector', () => {
1878
1947
  });
1879
1948
 
1880
1949
  describe('Schema Support', () => {
1881
- const customSchema = 'mastra_test';
1950
+ const customSchema = 'mastraTest';
1882
1951
  let vectorDB: PgVector;
1883
1952
  let customSchemaVectorDB: PgVector;
1884
1953
 
@@ -125,8 +125,17 @@ export class PgVector extends MastraVector {
125
125
 
126
126
  private getTableName(indexName: string) {
127
127
  const parsedIndexName = parseSqlIdentifier(indexName, 'index name');
128
- const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, 'schema name') : undefined;
129
- return parsedSchemaName ? `${parsedSchemaName}.${parsedIndexName}` : parsedIndexName;
128
+ const quotedIndexName = `"${parsedIndexName}"`;
129
+ const quotedSchemaName = this.getSchemaName();
130
+ const quotedVectorName = `"${parsedIndexName}_vector_idx"`;
131
+ return {
132
+ tableName: quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName,
133
+ vectorIndexName: quotedVectorName,
134
+ };
135
+ }
136
+
137
+ private getSchemaName() {
138
+ return this.schema ? `"${parseSqlIdentifier(this.schema, 'schema name')}"` : undefined;
130
139
  }
131
140
 
132
141
  transformFilter(filter?: VectorFilter) {
@@ -179,7 +188,7 @@ export class PgVector extends MastraVector {
179
188
  await client.query(`SET LOCAL ivfflat.probes = ${probes}`);
180
189
  }
181
190
 
182
- const tableName = this.getTableName(indexName);
191
+ const { tableName } = this.getTableName(indexName);
183
192
 
184
193
  const query = `
185
194
  WITH vector_scores AS (
@@ -210,7 +219,7 @@ export class PgVector extends MastraVector {
210
219
  }
211
220
 
212
221
  async upsert({ indexName, vectors, metadata, ids }: UpsertVectorParams): Promise<string[]> {
213
- const tableName = this.getTableName(indexName);
222
+ const { tableName } = this.getTableName(indexName);
214
223
 
215
224
  // Start a transaction
216
225
  const client = await this.pool.connect();
@@ -289,7 +298,7 @@ export class PgVector extends MastraVector {
289
298
 
290
299
  if (!schemaExists) {
291
300
  try {
292
- await client.query(`CREATE SCHEMA IF NOT EXISTS ${this.schema}`);
301
+ await client.query(`CREATE SCHEMA IF NOT EXISTS ${this.getSchemaName()}`);
293
302
  this.logger.info(`Schema "${this.schema}" created successfully`);
294
303
  } catch (error) {
295
304
  this.logger.error(`Failed to create schema "${this.schema}"`, { error });
@@ -324,7 +333,7 @@ export class PgVector extends MastraVector {
324
333
  indexConfig = {},
325
334
  buildIndex = true,
326
335
  }: PgCreateIndexParams): Promise<void> {
327
- const tableName = this.getTableName(indexName);
336
+ const { tableName } = this.getTableName(indexName);
328
337
 
329
338
  // Validate inputs
330
339
  if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
@@ -391,10 +400,10 @@ export class PgVector extends MastraVector {
391
400
  const mutex = this.getMutexByName(`build-${indexName}`);
392
401
  // Use async-mutex instead of advisory lock for perf (over 2x as fast)
393
402
  await mutex.runExclusive(async () => {
394
- const tableName = this.getTableName(indexName);
403
+ const { tableName, vectorIndexName } = this.getTableName(indexName);
395
404
 
396
405
  if (this.createdIndexes.has(indexName)) {
397
- await client.query(`DROP INDEX IF EXISTS ${tableName}_vector_idx`);
406
+ await client.query(`DROP INDEX IF EXISTS ${vectorIndexName}`);
398
407
  }
399
408
 
400
409
  if (indexConfig.type === 'flat') {
@@ -411,7 +420,7 @@ export class PgVector extends MastraVector {
411
420
  const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
412
421
 
413
422
  indexSQL = `
414
- CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
423
+ CREATE INDEX IF NOT EXISTS ${vectorIndexName}
415
424
  ON ${tableName}
416
425
  USING hnsw (embedding ${metricOp})
417
426
  WITH (
@@ -428,7 +437,7 @@ export class PgVector extends MastraVector {
428
437
  lists = Math.max(100, Math.min(4000, Math.floor(Math.sqrt(size) * 2)));
429
438
  }
430
439
  indexSQL = `
431
- CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
440
+ CREATE INDEX IF NOT EXISTS ${vectorIndexName}
432
441
  ON ${tableName}
433
442
  USING ivfflat (embedding ${metricOp})
434
443
  WITH (lists = ${lists});
@@ -517,7 +526,7 @@ export class PgVector extends MastraVector {
517
526
  async describeIndex({ indexName }: DescribeIndexParams): Promise<PGIndexStats> {
518
527
  const client = await this.pool.connect();
519
528
  try {
520
- const tableName = this.getTableName(indexName);
529
+ const { tableName } = this.getTableName(indexName);
521
530
 
522
531
  // Check if table exists with a vector column
523
532
  const tableExistsQuery = `
@@ -613,7 +622,7 @@ export class PgVector extends MastraVector {
613
622
  async deleteIndex({ indexName }: DeleteIndexParams): Promise<void> {
614
623
  const client = await this.pool.connect();
615
624
  try {
616
- const tableName = this.getTableName(indexName);
625
+ const { tableName } = this.getTableName(indexName);
617
626
  // Drop the table
618
627
  await client.query(`DROP TABLE IF EXISTS ${tableName} CASCADE`);
619
628
  this.createdIndexes.delete(indexName);
@@ -628,7 +637,7 @@ export class PgVector extends MastraVector {
628
637
  async truncateIndex({ indexName }: DeleteIndexParams): Promise<void> {
629
638
  const client = await this.pool.connect();
630
639
  try {
631
- const tableName = this.getTableName(indexName);
640
+ const { tableName } = this.getTableName(indexName);
632
641
  await client.query(`TRUNCATE ${tableName}`);
633
642
  } catch (e: any) {
634
643
  await client.query('ROLLBACK');
@@ -678,7 +687,7 @@ export class PgVector extends MastraVector {
678
687
  return;
679
688
  }
680
689
 
681
- const tableName = this.getTableName(indexName);
690
+ const { tableName } = this.getTableName(indexName);
682
691
 
683
692
  // query looks like this:
684
693
  // UPDATE table SET embedding = $2::vector, metadata = $3::jsonb WHERE id = $1
@@ -706,7 +715,7 @@ export class PgVector extends MastraVector {
706
715
  async deleteVector({ indexName, id }: DeleteVectorParams): Promise<void> {
707
716
  const client = await this.pool.connect();
708
717
  try {
709
- const tableName = this.getTableName(indexName);
718
+ const { tableName } = this.getTableName(indexName);
710
719
  const query = `
711
720
  DELETE FROM ${tableName}
712
721
  WHERE vector_id = $1