@mastra/pg 0.3.4 → 0.4.0-alpha.1

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/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
1
2
  import { MastraVector } from '@mastra/core/vector';
2
3
  import { Mutex } from 'async-mutex';
3
4
  import pg from 'pg';
@@ -73,22 +74,26 @@ var PGFilterTranslator = class extends BaseFilterTranslator {
73
74
  return { $regex: flags ? `(?${flags})${pattern}` : pattern };
74
75
  }
75
76
  };
76
-
77
- // src/vector/sql-builder.ts
78
77
  var createBasicOperator = (symbol) => {
79
- return (key, paramIndex) => ({
80
- sql: `CASE
81
- WHEN $${paramIndex}::text IS NULL THEN metadata#>>'{${handleKey(key)}}' IS ${symbol === "=" ? "" : "NOT"} NULL
82
- ELSE metadata#>>'{${handleKey(key)}}' ${symbol} $${paramIndex}::text
83
- END`,
84
- needsValue: true
85
- });
78
+ return (key, paramIndex) => {
79
+ const jsonPathKey = parseJsonPathKey(key);
80
+ return {
81
+ sql: `CASE
82
+ WHEN $${paramIndex}::text IS NULL THEN metadata#>>'{${jsonPathKey}}' IS ${symbol === "=" ? "" : "NOT"} NULL
83
+ ELSE metadata#>>'{${jsonPathKey}}' ${symbol} $${paramIndex}::text
84
+ END`,
85
+ needsValue: true
86
+ };
87
+ };
86
88
  };
87
89
  var createNumericOperator = (symbol) => {
88
- return (key, paramIndex) => ({
89
- sql: `(metadata#>>'{${handleKey(key)}}')::numeric ${symbol} $${paramIndex}`,
90
- needsValue: true
91
- });
90
+ return (key, paramIndex) => {
91
+ const jsonPathKey = parseJsonPathKey(key);
92
+ return {
93
+ sql: `(metadata#>>'{${jsonPathKey}}')::numeric ${symbol} $${paramIndex}`,
94
+ needsValue: true
95
+ };
96
+ };
92
97
  };
93
98
  function buildElemMatchConditions(value, paramIndex) {
94
99
  if (typeof value !== "object" || Array.isArray(value)) {
@@ -139,46 +144,56 @@ var FILTER_OPERATORS = {
139
144
  $lt: createNumericOperator("<"),
140
145
  $lte: createNumericOperator("<="),
141
146
  // Array Operators
142
- $in: (key, paramIndex) => ({
143
- sql: `(
144
- CASE
145
- WHEN jsonb_typeof(metadata->'${handleKey(key)}') = 'array' THEN
146
- EXISTS (
147
- SELECT 1 FROM jsonb_array_elements_text(metadata->'${handleKey(key)}') as elem
148
- WHERE elem = ANY($${paramIndex}::text[])
149
- )
150
- ELSE metadata#>>'{${handleKey(key)}}' = ANY($${paramIndex}::text[])
151
- END
152
- )`,
153
- needsValue: true
154
- }),
155
- $nin: (key, paramIndex) => ({
156
- sql: `(
157
- CASE
158
- WHEN jsonb_typeof(metadata->'${handleKey(key)}') = 'array' THEN
159
- NOT EXISTS (
160
- SELECT 1 FROM jsonb_array_elements_text(metadata->'${handleKey(key)}') as elem
161
- WHERE elem = ANY($${paramIndex}::text[])
162
- )
163
- ELSE metadata#>>'{${handleKey(key)}}' != ALL($${paramIndex}::text[])
164
- END
165
- )`,
166
- needsValue: true
167
- }),
168
- $all: (key, paramIndex) => ({
169
- sql: `CASE WHEN array_length($${paramIndex}::text[], 1) IS NULL THEN false
170
- ELSE (metadata#>'{${handleKey(key)}}')::jsonb ?& $${paramIndex}::text[] END`,
171
- needsValue: true
172
- }),
147
+ $in: (key, paramIndex) => {
148
+ const jsonPathKey = parseJsonPathKey(key);
149
+ return {
150
+ sql: `(
151
+ CASE
152
+ WHEN jsonb_typeof(metadata->'${jsonPathKey}') = 'array' THEN
153
+ EXISTS (
154
+ SELECT 1 FROM jsonb_array_elements_text(metadata->'${jsonPathKey}') as elem
155
+ WHERE elem = ANY($${paramIndex}::text[])
156
+ )
157
+ ELSE metadata#>>'{${jsonPathKey}}' = ANY($${paramIndex}::text[])
158
+ END
159
+ )`,
160
+ needsValue: true
161
+ };
162
+ },
163
+ $nin: (key, paramIndex) => {
164
+ const jsonPathKey = parseJsonPathKey(key);
165
+ return {
166
+ sql: `(
167
+ CASE
168
+ WHEN jsonb_typeof(metadata->'${jsonPathKey}') = 'array' THEN
169
+ NOT EXISTS (
170
+ SELECT 1 FROM jsonb_array_elements_text(metadata->'${jsonPathKey}') as elem
171
+ WHERE elem = ANY($${paramIndex}::text[])
172
+ )
173
+ ELSE metadata#>>'{${jsonPathKey}}' != ALL($${paramIndex}::text[])
174
+ END
175
+ )`,
176
+ needsValue: true
177
+ };
178
+ },
179
+ $all: (key, paramIndex) => {
180
+ const jsonPathKey = parseJsonPathKey(key);
181
+ return {
182
+ sql: `CASE WHEN array_length($${paramIndex}::text[], 1) IS NULL THEN false
183
+ ELSE (metadata#>'{${jsonPathKey}}')::jsonb ?& $${paramIndex}::text[] END`,
184
+ needsValue: true
185
+ };
186
+ },
173
187
  $elemMatch: (key, paramIndex, value) => {
174
188
  const { sql, values } = buildElemMatchConditions(value, paramIndex);
189
+ const jsonPathKey = parseJsonPathKey(key);
175
190
  return {
176
191
  sql: `(
177
192
  CASE
178
- WHEN jsonb_typeof(metadata->'${handleKey(key)}') = 'array' THEN
193
+ WHEN jsonb_typeof(metadata->'${jsonPathKey}') = 'array' THEN
179
194
  EXISTS (
180
195
  SELECT 1
181
- FROM jsonb_array_elements(metadata->'${handleKey(key)}') as elem
196
+ FROM jsonb_array_elements(metadata->'${jsonPathKey}') as elem
182
197
  WHERE ${sql}
183
198
  )
184
199
  ELSE FALSE
@@ -189,33 +204,40 @@ var FILTER_OPERATORS = {
189
204
  };
190
205
  },
191
206
  // Element Operators
192
- $exists: (key) => ({
193
- sql: `metadata ? '${key}'`,
194
- needsValue: false
195
- }),
207
+ $exists: (key) => {
208
+ const jsonPathKey = parseJsonPathKey(key);
209
+ return {
210
+ sql: `metadata ? '${jsonPathKey}'`,
211
+ needsValue: false
212
+ };
213
+ },
196
214
  // Logical Operators
197
215
  $and: (key) => ({ sql: `(${key})`, needsValue: false }),
198
216
  $or: (key) => ({ sql: `(${key})`, needsValue: false }),
199
217
  $not: (key) => ({ sql: `NOT (${key})`, needsValue: false }),
200
218
  $nor: (key) => ({ sql: `NOT (${key})`, needsValue: false }),
201
219
  // Regex Operators
202
- $regex: (key, paramIndex) => ({
203
- sql: `metadata#>>'{${handleKey(key)}}' ~ $${paramIndex}`,
204
- needsValue: true
205
- }),
220
+ $regex: (key, paramIndex) => {
221
+ const jsonPathKey = parseJsonPathKey(key);
222
+ return {
223
+ sql: `metadata#>>'{${jsonPathKey}}' ~ $${paramIndex}`,
224
+ needsValue: true
225
+ };
226
+ },
206
227
  $contains: (key, paramIndex, value) => {
228
+ const jsonPathKey = parseJsonPathKey(key);
207
229
  let sql;
208
230
  if (Array.isArray(value)) {
209
- sql = `(metadata->'${handleKey(key)}') ?& $${paramIndex}`;
231
+ sql = `(metadata->'${jsonPathKey}') ?& $${paramIndex}`;
210
232
  } else if (typeof value === "string") {
211
- sql = `metadata->>'${handleKey(key)}' ILIKE '%' || $${paramIndex} || '%'`;
233
+ sql = `metadata->>'${jsonPathKey}' ILIKE '%' || $${paramIndex} || '%' ESCAPE '\\'`;
212
234
  } else {
213
- sql = `metadata->>'${handleKey(key)}' = $${paramIndex}`;
235
+ sql = `metadata->>'${jsonPathKey}' = $${paramIndex}`;
214
236
  }
215
237
  return {
216
238
  sql,
217
239
  needsValue: true,
218
- transformValue: () => Array.isArray(value) ? value.map(String) : value
240
+ transformValue: () => Array.isArray(value) ? value.map(String) : typeof value === "string" ? escapeLikePattern(value) : value
219
241
  };
220
242
  },
221
243
  /**
@@ -230,29 +252,36 @@ var FILTER_OPERATORS = {
230
252
  // return JSON.stringify(parts.reduceRight((value, key) => ({ [key]: value }), value));
231
253
  // },
232
254
  // }),
233
- $size: (key, paramIndex) => ({
234
- sql: `(
255
+ $size: (key, paramIndex) => {
256
+ const jsonPathKey = parseJsonPathKey(key);
257
+ return {
258
+ sql: `(
235
259
  CASE
236
- WHEN jsonb_typeof(metadata#>'{${handleKey(key)}}') = 'array' THEN
237
- jsonb_array_length(metadata#>'{${handleKey(key)}}') = $${paramIndex}
260
+ WHEN jsonb_typeof(metadata#>'{${jsonPathKey}}') = 'array' THEN
261
+ jsonb_array_length(metadata#>'{${jsonPathKey}}') = $${paramIndex}
238
262
  ELSE FALSE
239
263
  END
240
264
  )`,
241
- needsValue: true
242
- })
265
+ needsValue: true
266
+ };
267
+ }
243
268
  };
244
- var handleKey = (key) => {
245
- return key.replace(/\./g, ",");
269
+ var parseJsonPathKey = (key) => {
270
+ const parsedKey = key !== "" ? parseFieldKey(key) : "";
271
+ return parsedKey.replace(/\./g, ",");
246
272
  };
247
- function buildFilterQuery(filter, minScore) {
248
- const values = [minScore];
273
+ function escapeLikePattern(str) {
274
+ return str.replace(/([%_\\])/g, "\\$1");
275
+ }
276
+ function buildFilterQuery(filter, minScore, topK) {
277
+ const values = [minScore, topK];
249
278
  function buildCondition(key, value, parentPath) {
250
279
  if (["$and", "$or", "$not", "$nor"].includes(key)) {
251
280
  return handleLogicalOperator(key, value);
252
281
  }
253
282
  if (!value || typeof value !== "object") {
254
283
  values.push(value);
255
- return `metadata#>>'{${handleKey(key)}}' = $${values.length}`;
284
+ return `metadata#>>'{${parseJsonPathKey(key)}}' = $${values.length}`;
256
285
  }
257
286
  const [[operator, operatorValue] = []] = Object.entries(value);
258
287
  if (operator === "$not") {
@@ -262,7 +291,7 @@ function buildFilterQuery(filter, minScore) {
262
291
  throw new Error(`Invalid operator in $not condition: ${nestedOp}`);
263
292
  }
264
293
  const operatorFn2 = FILTER_OPERATORS[nestedOp];
265
- const operatorResult2 = operatorFn2(key, values.length + 1);
294
+ const operatorResult2 = operatorFn2(key, values.length + 1, nestedValue);
266
295
  if (operatorResult2.needsValue) {
267
296
  values.push(nestedValue);
268
297
  }
@@ -333,27 +362,11 @@ var PgVector = class extends MastraVector {
333
362
  installVectorExtensionPromise = null;
334
363
  vectorExtensionInstalled = void 0;
335
364
  schemaSetupComplete = void 0;
336
- constructor(config) {
337
- let connectionString;
338
- let pgPoolOptions;
339
- let schemaName;
340
- if (typeof config === "string") {
341
- console.warn(
342
- `DEPRECATION WARNING: Passing connectionString as a string to PgVector constructor is deprecated.
343
-
344
- Please use an object parameter instead:
345
- new PgVector({ connectionString })
346
-
347
- The string signature will be removed on May 20th, 2025.`
348
- );
349
- connectionString = config;
350
- schemaName = void 0;
351
- pgPoolOptions = void 0;
352
- } else {
353
- connectionString = config.connectionString;
354
- schemaName = config.schemaName;
355
- pgPoolOptions = config.pgPoolOptions;
356
- }
365
+ constructor({
366
+ connectionString,
367
+ schemaName,
368
+ pgPoolOptions
369
+ }) {
357
370
  if (!connectionString || connectionString.trim() === "") {
358
371
  throw new Error(
359
372
  "PgVector: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults."
@@ -381,7 +394,7 @@ var PgVector = class extends MastraVector {
381
394
  void (async () => {
382
395
  const existingIndexes = await this.listIndexes();
383
396
  void existingIndexes.map(async (indexName) => {
384
- const info = await this.getIndexInfo(indexName);
397
+ const info = await this.getIndexInfo({ indexName });
385
398
  const key = await this.getIndexCacheKey({
386
399
  indexName,
387
400
  metric: info.metric,
@@ -397,31 +410,42 @@ var PgVector = class extends MastraVector {
397
410
  return this.mutexesByName.get(indexName);
398
411
  }
399
412
  getTableName(indexName) {
400
- return this.schema ? `${this.schema}.${indexName}` : indexName;
413
+ const parsedIndexName = parseSqlIdentifier(indexName, "index name");
414
+ const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, "schema name") : void 0;
415
+ return parsedSchemaName ? `${parsedSchemaName}.${parsedIndexName}` : parsedIndexName;
401
416
  }
402
417
  transformFilter(filter) {
403
418
  const translator = new PGFilterTranslator();
404
419
  return translator.translate(filter);
405
420
  }
406
- async getIndexInfo(indexName) {
421
+ async getIndexInfo({ indexName }) {
407
422
  if (!this.describeIndexCache.has(indexName)) {
408
- this.describeIndexCache.set(indexName, await this.describeIndex(indexName));
423
+ this.describeIndexCache.set(indexName, await this.describeIndex({ indexName }));
409
424
  }
410
425
  return this.describeIndexCache.get(indexName);
411
426
  }
412
- async query(...args) {
413
- const params = this.normalizeArgs("query", args, [
414
- "minScore",
415
- "ef",
416
- "probes"
417
- ]);
418
- const { indexName, queryVector, topK = 10, filter, includeVector = false, minScore = 0, ef, probes } = params;
427
+ async query({
428
+ indexName,
429
+ queryVector,
430
+ topK = 10,
431
+ filter,
432
+ includeVector = false,
433
+ minScore = 0,
434
+ ef,
435
+ probes
436
+ }) {
437
+ if (!Number.isInteger(topK) || topK <= 0) {
438
+ throw new Error("topK must be a positive integer");
439
+ }
440
+ if (!Array.isArray(queryVector) || !queryVector.every((x) => typeof x === "number" && Number.isFinite(x))) {
441
+ throw new Error("queryVector must be an array of finite numbers");
442
+ }
419
443
  const client = await this.pool.connect();
420
444
  try {
421
445
  const vectorStr = `[${queryVector.join(",")}]`;
422
446
  const translatedFilter = this.transformFilter(filter);
423
- const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter, minScore);
424
- const indexInfo = await this.getIndexInfo(indexName);
447
+ const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter, minScore, topK);
448
+ const indexInfo = await this.getIndexInfo({ indexName });
425
449
  if (indexInfo.type === "hnsw") {
426
450
  const calculatedEf = ef ?? Math.max(topK, (indexInfo?.config?.m ?? 16) * topK);
427
451
  const searchEf = Math.min(1e3, Math.max(1, calculatedEf));
@@ -445,7 +469,7 @@ var PgVector = class extends MastraVector {
445
469
  FROM vector_scores
446
470
  WHERE score > $1
447
471
  ORDER BY score DESC
448
- LIMIT ${topK}`;
472
+ LIMIT $2`;
449
473
  const result = await client.query(query, filterValues);
450
474
  return result.rows.map(({ id, score, metadata, embedding }) => ({
451
475
  id,
@@ -457,9 +481,7 @@ var PgVector = class extends MastraVector {
457
481
  client.release();
458
482
  }
459
483
  }
460
- async upsert(...args) {
461
- const params = this.normalizeArgs("upsert", args);
462
- const { indexName, vectors, metadata, ids } = params;
484
+ async upsert({ indexName, vectors, metadata, ids }) {
463
485
  const tableName = this.getTableName(indexName);
464
486
  const client = await this.pool.connect();
465
487
  try {
@@ -486,7 +508,7 @@ var PgVector = class extends MastraVector {
486
508
  if (match) {
487
509
  const [, expected, actual] = match;
488
510
  throw new Error(
489
- `Vector dimension mismatch: Index "${params.indexName}" expects ${expected} dimensions but got ${actual} dimensions. Either use a matching embedding model or delete and recreate the index with the new dimension.`
511
+ `Vector dimension mismatch: Index "${indexName}" expects ${expected} dimensions but got ${actual} dimensions. Either use a matching embedding model or delete and recreate the index with the new dimension.`
490
512
  );
491
513
  }
492
514
  }
@@ -496,8 +518,13 @@ var PgVector = class extends MastraVector {
496
518
  }
497
519
  }
498
520
  hasher = xxhash();
499
- async getIndexCacheKey(params) {
500
- const input = params.indexName + params.dimension + params.metric + (params.type || "ivfflat");
521
+ async getIndexCacheKey({
522
+ indexName,
523
+ dimension,
524
+ metric,
525
+ type
526
+ }) {
527
+ const input = indexName + dimension + metric + (type || "ivfflat");
501
528
  return (await this.hasher).h32(input);
502
529
  }
503
530
  cachedIndexExists(indexName, newKey) {
@@ -545,12 +572,13 @@ var PgVector = class extends MastraVector {
545
572
  }
546
573
  await this.setupSchemaPromise;
547
574
  }
548
- async createIndex(...args) {
549
- const params = this.normalizeArgs("createIndex", args, [
550
- "indexConfig",
551
- "buildIndex"
552
- ]);
553
- const { indexName, dimension, metric = "cosine", indexConfig = {}, buildIndex = true } = params;
575
+ async createIndex({
576
+ indexName,
577
+ dimension,
578
+ metric = "cosine",
579
+ indexConfig = {},
580
+ buildIndex = true
581
+ }) {
554
582
  const tableName = this.getTableName(indexName);
555
583
  if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
556
584
  throw new Error("Invalid index name format");
@@ -591,20 +619,7 @@ var PgVector = class extends MastraVector {
591
619
  }
592
620
  });
593
621
  }
594
- /**
595
- * @deprecated This function is deprecated. Use buildIndex instead
596
- * This function will be removed on May 20th, 2025
597
- */
598
- async defineIndex(indexName, metric = "cosine", indexConfig) {
599
- console.warn("defineIndex is deprecated. Use buildIndex instead. This function will be removed on May 20th, 2025");
600
- return this.buildIndex({ indexName, metric, indexConfig });
601
- }
602
- async buildIndex(...args) {
603
- const params = this.normalizeArgs("buildIndex", args, [
604
- "metric",
605
- "indexConfig"
606
- ]);
607
- const { indexName, metric = "cosine", indexConfig } = params;
622
+ async buildIndex({ indexName, metric = "cosine", indexConfig }) {
608
623
  const client = await this.pool.connect();
609
624
  try {
610
625
  await this.setupIndex({ indexName, metric, indexConfig }, client);
@@ -708,7 +723,13 @@ var PgVector = class extends MastraVector {
708
723
  client.release();
709
724
  }
710
725
  }
711
- async describeIndex(indexName) {
726
+ /**
727
+ * Retrieves statistics about a vector index.
728
+ *
729
+ * @param {string} indexName - The name of the index to describe
730
+ * @returns A promise that resolves to the index statistics including dimension, count and metric
731
+ */
732
+ async describeIndex({ indexName }) {
712
733
  const client = await this.pool.connect();
713
734
  try {
714
735
  const tableName = this.getTableName(indexName);
@@ -782,7 +803,7 @@ var PgVector = class extends MastraVector {
782
803
  client.release();
783
804
  }
784
805
  }
785
- async deleteIndex(indexName) {
806
+ async deleteIndex({ indexName }) {
786
807
  const client = await this.pool.connect();
787
808
  try {
788
809
  const tableName = this.getTableName(indexName);
@@ -795,7 +816,7 @@ var PgVector = class extends MastraVector {
795
816
  client.release();
796
817
  }
797
818
  }
798
- async truncateIndex(indexName) {
819
+ async truncateIndex({ indexName }) {
799
820
  const client = await this.pool.connect();
800
821
  try {
801
822
  const tableName = this.getTableName(indexName);
@@ -811,8 +832,6 @@ var PgVector = class extends MastraVector {
811
832
  await this.pool.end();
812
833
  }
813
834
  /**
814
- * @deprecated Use {@link updateVector} instead. This method will be removed on May 20th, 2025.
815
- *
816
835
  * Updates a vector by its ID with the provided vector and/or metadata.
817
836
  * @param indexName - The name of the index containing the vector.
818
837
  * @param id - The ID of the vector to update.
@@ -822,25 +841,7 @@ var PgVector = class extends MastraVector {
822
841
  * @returns A promise that resolves when the update is complete.
823
842
  * @throws Will throw an error if no updates are provided or if the update operation fails.
824
843
  */
825
- async updateIndexById(indexName, id, update) {
826
- this.logger.warn(
827
- `Deprecation Warning: updateIndexById() is deprecated.
828
- Please use updateVector() instead.
829
- updateIndexById() will be removed on May 20th, 2025.`
830
- );
831
- await this.updateVector(indexName, id, update);
832
- }
833
- /**
834
- * Updates a vector by its ID with the provided vector and/or metadata.
835
- * @param indexName - The name of the index containing the vector.
836
- * @param id - The ID of the vector to update.
837
- * @param update - An object containing the vector and/or metadata to update.
838
- * @param update.vector - An optional array of numbers representing the new vector.
839
- * @param update.metadata - An optional record containing the new metadata.
840
- * @returns A promise that resolves when the update is complete.
841
- * @throws Will throw an error if no updates are provided or if the update operation fails.
842
- */
843
- async updateVector(indexName, id, update) {
844
+ async updateVector({ indexName, id, update }) {
844
845
  if (!update.vector && !update.metadata) {
845
846
  throw new Error("No updates provided");
846
847
  }
@@ -874,23 +875,6 @@ var PgVector = class extends MastraVector {
874
875
  client.release();
875
876
  }
876
877
  }
877
- /**
878
- * @deprecated Use {@link deleteVector} instead. This method will be removed on May 20th, 2025.
879
- *
880
- * Deletes a vector by its ID.
881
- * @param indexName - The name of the index containing the vector.
882
- * @param id - The ID of the vector to delete.
883
- * @returns A promise that resolves when the deletion is complete.
884
- * @throws Will throw an error if the deletion operation fails.
885
- */
886
- async deleteIndexById(indexName, id) {
887
- this.logger.warn(
888
- `Deprecation Warning: deleteIndexById() is deprecated.
889
- Please use deleteVector() instead.
890
- deleteIndexById() will be removed on May 20th, 2025.`
891
- );
892
- await this.deleteVector(indexName, id);
893
- }
894
878
  /**
895
879
  * Deletes a vector by its ID.
896
880
  * @param indexName - The name of the index containing the vector.
@@ -898,7 +882,7 @@ var PgVector = class extends MastraVector {
898
882
  * @returns A promise that resolves when the deletion is complete.
899
883
  * @throws Will throw an error if the deletion operation fails.
900
884
  */
901
- async deleteVector(indexName, id) {
885
+ async deleteVector({ indexName, id }) {
902
886
  const client = await this.pool.connect();
903
887
  try {
904
888
  const tableName = this.getTableName(indexName);
@@ -939,12 +923,7 @@ var PostgresStore = class extends MastraStorage {
939
923
  }
940
924
  super({ name: "PostgresStore" });
941
925
  this.pgp = pgPromise();
942
- if ("schema" in config && config.schema) {
943
- console.warn(
944
- '[DEPRECATION NOTICE] The "schema" option in PostgresStore is deprecated. Please use "schemaName" instead. Support for "schema" will be removed on May 20th, 2025.'
945
- );
946
- }
947
- this.schema = config.schemaName ?? config.schema;
926
+ this.schema = config.schemaName;
948
927
  this.db = this.pgp(
949
928
  `connectionString` in config ? { connectionString: config.connectionString } : {
950
929
  host: config.host,
@@ -957,7 +936,9 @@ var PostgresStore = class extends MastraStorage {
957
936
  );
958
937
  }
959
938
  getTableName(indexName) {
960
- return this.schema ? `${this.schema}."${indexName}"` : `"${indexName}"`;
939
+ const parsedIndexName = parseSqlIdentifier(indexName, "table name");
940
+ const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, "schema name") : void 0;
941
+ return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
961
942
  }
962
943
  async getEvalsByAgentName(agentName, type) {
963
944
  try {
@@ -1032,12 +1013,14 @@ var PostgresStore = class extends MastraStorage {
1032
1013
  }
1033
1014
  if (attributes) {
1034
1015
  Object.keys(attributes).forEach((key) => {
1035
- conditions.push(`attributes->>'${key}' = $${idx++}`);
1016
+ const parsedKey = parseSqlIdentifier(key, "attribute key");
1017
+ conditions.push(`attributes->>'${parsedKey}' = $${idx++}`);
1036
1018
  });
1037
1019
  }
1038
1020
  if (filters) {
1039
1021
  Object.entries(filters).forEach(([key]) => {
1040
- conditions.push(`${key} = $${idx++}`);
1022
+ const parsedKey = parseSqlIdentifier(key, "filter key");
1023
+ conditions.push(`${parsedKey} = $${idx++}`);
1041
1024
  });
1042
1025
  }
1043
1026
  if (fromDate) {
@@ -1139,10 +1122,11 @@ var PostgresStore = class extends MastraStorage {
1139
1122
  }) {
1140
1123
  try {
1141
1124
  const columns = Object.entries(schema).map(([name, def]) => {
1125
+ const parsedName = parseSqlIdentifier(name, "column name");
1142
1126
  const constraints = [];
1143
1127
  if (def.primaryKey) constraints.push("PRIMARY KEY");
1144
1128
  if (!def.nullable) constraints.push("NOT NULL");
1145
- return `"${name}" ${def.type.toUpperCase()} ${constraints.join(" ")}`;
1129
+ return `"${parsedName}" ${def.type.toUpperCase()} ${constraints.join(" ")}`;
1146
1130
  }).join(",\n");
1147
1131
  if (this.schema) {
1148
1132
  await this.setupSchema();
@@ -1179,7 +1163,7 @@ var PostgresStore = class extends MastraStorage {
1179
1163
  }
1180
1164
  async insert({ tableName, record }) {
1181
1165
  try {
1182
- const columns = Object.keys(record);
1166
+ const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
1183
1167
  const values = Object.values(record);
1184
1168
  const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
1185
1169
  await this.db.none(
@@ -1193,7 +1177,7 @@ var PostgresStore = class extends MastraStorage {
1193
1177
  }
1194
1178
  async load({ tableName, keys }) {
1195
1179
  try {
1196
- const keyEntries = Object.entries(keys);
1180
+ const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
1197
1181
  const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(" AND ");
1198
1182
  const values = keyEntries.map(([_, value]) => value);
1199
1183
  const result = await this.db.oneOrNone(
@@ -3,19 +3,19 @@ services:
3
3
  image: pgvector/pgvector:pg16
4
4
  container_name: 'pg-perf-test-db'
5
5
  ports:
6
- - '5435:5432'
6
+ - '5435:5432'
7
7
  environment:
8
8
  POSTGRES_USER: ${POSTGRES_USER:-postgres}
9
9
  POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
10
10
  POSTGRES_DB: ${POSTGRES_DB:-mastra}
11
11
  shm_size: 1gb
12
12
  command:
13
- - "postgres"
14
- - "-c"
15
- - "shared_buffers=512MB"
16
- - "-c"
17
- - "maintenance_work_mem=1024MB"
18
- - "-c"
19
- - "work_mem=512MB"
13
+ - 'postgres'
14
+ - '-c'
15
+ - 'shared_buffers=512MB'
16
+ - '-c'
17
+ - 'maintenance_work_mem=1024MB'
18
+ - '-c'
19
+ - 'work_mem=512MB'
20
20
  tmpfs:
21
- - /var/lib/postgresql/data
21
+ - /var/lib/postgresql/data
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/pg",
3
- "version": "0.3.4",
3
+ "version": "0.4.0-alpha.1",
4
4
  "description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,8 +23,7 @@
23
23
  "async-mutex": "^0.5.0",
24
24
  "pg": "^8.13.3",
25
25
  "pg-promise": "^11.11.0",
26
- "xxhash-wasm": "^1.1.0",
27
- "@mastra/core": "^0.9.4"
26
+ "xxhash-wasm": "^1.1.0"
28
27
  },
29
28
  "devDependencies": {
30
29
  "@microsoft/api-extractor": "^7.52.5",
@@ -34,7 +33,11 @@
34
33
  "tsup": "^8.4.0",
35
34
  "typescript": "^5.8.2",
36
35
  "vitest": "^3.1.2",
37
- "@internal/lint": "0.0.5"
36
+ "@internal/lint": "0.0.5",
37
+ "@mastra/core": "0.10.0-alpha.1"
38
+ },
39
+ "peerDependencies": {
40
+ "@mastra/core": "^0.9.4"
38
41
  },
39
42
  "scripts": {
40
43
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",