@mastra/pg 0.3.4 → 0.3.5-alpha.0

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
  }
@@ -381,7 +410,7 @@ var PgVector = class extends MastraVector {
381
410
  void (async () => {
382
411
  const existingIndexes = await this.listIndexes();
383
412
  void existingIndexes.map(async (indexName) => {
384
- const info = await this.getIndexInfo(indexName);
413
+ const info = await this.getIndexInfo({ indexName });
385
414
  const key = await this.getIndexCacheKey({
386
415
  indexName,
387
416
  metric: info.metric,
@@ -397,15 +426,19 @@ var PgVector = class extends MastraVector {
397
426
  return this.mutexesByName.get(indexName);
398
427
  }
399
428
  getTableName(indexName) {
400
- return this.schema ? `${this.schema}.${indexName}` : indexName;
429
+ const parsedIndexName = parseSqlIdentifier(indexName, "index name");
430
+ const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, "schema name") : void 0;
431
+ return parsedSchemaName ? `${parsedSchemaName}.${parsedIndexName}` : parsedIndexName;
401
432
  }
402
433
  transformFilter(filter) {
403
434
  const translator = new PGFilterTranslator();
404
435
  return translator.translate(filter);
405
436
  }
406
- async getIndexInfo(indexName) {
437
+ async getIndexInfo(...args) {
438
+ const params = this.normalizeArgs("getIndexInfo", args);
439
+ const { indexName } = params;
407
440
  if (!this.describeIndexCache.has(indexName)) {
408
- this.describeIndexCache.set(indexName, await this.describeIndex(indexName));
441
+ this.describeIndexCache.set(indexName, await this.describeIndex({ indexName }));
409
442
  }
410
443
  return this.describeIndexCache.get(indexName);
411
444
  }
@@ -416,12 +449,18 @@ var PgVector = class extends MastraVector {
416
449
  "probes"
417
450
  ]);
418
451
  const { indexName, queryVector, topK = 10, filter, includeVector = false, minScore = 0, ef, probes } = params;
452
+ if (!Number.isInteger(topK) || topK <= 0) {
453
+ throw new Error("topK must be a positive integer");
454
+ }
455
+ if (!Array.isArray(queryVector) || !queryVector.every((x) => typeof x === "number" && Number.isFinite(x))) {
456
+ throw new Error("queryVector must be an array of finite numbers");
457
+ }
419
458
  const client = await this.pool.connect();
420
459
  try {
421
460
  const vectorStr = `[${queryVector.join(",")}]`;
422
461
  const translatedFilter = this.transformFilter(filter);
423
- const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter, minScore);
424
- const indexInfo = await this.getIndexInfo(indexName);
462
+ const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter, minScore, topK);
463
+ const indexInfo = await this.getIndexInfo({ indexName });
425
464
  if (indexInfo.type === "hnsw") {
426
465
  const calculatedEf = ef ?? Math.max(topK, (indexInfo?.config?.m ?? 16) * topK);
427
466
  const searchEf = Math.min(1e3, Math.max(1, calculatedEf));
@@ -445,7 +484,7 @@ var PgVector = class extends MastraVector {
445
484
  FROM vector_scores
446
485
  WHERE score > $1
447
486
  ORDER BY score DESC
448
- LIMIT ${topK}`;
487
+ LIMIT $2`;
449
488
  const result = await client.query(query, filterValues);
450
489
  return result.rows.map(({ id, score, metadata, embedding }) => ({
451
490
  id,
@@ -708,7 +747,16 @@ var PgVector = class extends MastraVector {
708
747
  client.release();
709
748
  }
710
749
  }
711
- async describeIndex(indexName) {
750
+ /**
751
+ * Retrieves statistics about a vector index.
752
+ *
753
+ * @param params - The parameters for describing an index
754
+ * @param params.indexName - The name of the index to describe
755
+ * @returns A promise that resolves to the index statistics including dimension, count and metric
756
+ */
757
+ async describeIndex(...args) {
758
+ const params = this.normalizeArgs("describeIndex", args);
759
+ const { indexName } = params;
712
760
  const client = await this.pool.connect();
713
761
  try {
714
762
  const tableName = this.getTableName(indexName);
@@ -782,7 +830,9 @@ var PgVector = class extends MastraVector {
782
830
  client.release();
783
831
  }
784
832
  }
785
- async deleteIndex(indexName) {
833
+ async deleteIndex(...args) {
834
+ const params = this.normalizeArgs("deleteIndex", args);
835
+ const { indexName } = params;
786
836
  const client = await this.pool.connect();
787
837
  try {
788
838
  const tableName = this.getTableName(indexName);
@@ -795,7 +845,9 @@ var PgVector = class extends MastraVector {
795
845
  client.release();
796
846
  }
797
847
  }
798
- async truncateIndex(indexName) {
848
+ async truncateIndex(...args) {
849
+ const params = this.normalizeArgs("truncateIndex", args);
850
+ const { indexName } = params;
799
851
  const client = await this.pool.connect();
800
852
  try {
801
853
  const tableName = this.getTableName(indexName);
@@ -828,7 +880,7 @@ var PgVector = class extends MastraVector {
828
880
  Please use updateVector() instead.
829
881
  updateIndexById() will be removed on May 20th, 2025.`
830
882
  );
831
- await this.updateVector(indexName, id, update);
883
+ await this.updateVector({ indexName, id, update });
832
884
  }
833
885
  /**
834
886
  * Updates a vector by its ID with the provided vector and/or metadata.
@@ -840,7 +892,9 @@ var PgVector = class extends MastraVector {
840
892
  * @returns A promise that resolves when the update is complete.
841
893
  * @throws Will throw an error if no updates are provided or if the update operation fails.
842
894
  */
843
- async updateVector(indexName, id, update) {
895
+ async updateVector(...args) {
896
+ const params = this.normalizeArgs("updateVector", args);
897
+ const { indexName, id, update } = params;
844
898
  if (!update.vector && !update.metadata) {
845
899
  throw new Error("No updates provided");
846
900
  }
@@ -889,7 +943,7 @@ var PgVector = class extends MastraVector {
889
943
  Please use deleteVector() instead.
890
944
  deleteIndexById() will be removed on May 20th, 2025.`
891
945
  );
892
- await this.deleteVector(indexName, id);
946
+ await this.deleteVector({ indexName, id });
893
947
  }
894
948
  /**
895
949
  * Deletes a vector by its ID.
@@ -898,7 +952,9 @@ var PgVector = class extends MastraVector {
898
952
  * @returns A promise that resolves when the deletion is complete.
899
953
  * @throws Will throw an error if the deletion operation fails.
900
954
  */
901
- async deleteVector(indexName, id) {
955
+ async deleteVector(...args) {
956
+ const params = this.normalizeArgs("deleteVector", args);
957
+ const { indexName, id } = params;
902
958
  const client = await this.pool.connect();
903
959
  try {
904
960
  const tableName = this.getTableName(indexName);
@@ -957,7 +1013,9 @@ var PostgresStore = class extends MastraStorage {
957
1013
  );
958
1014
  }
959
1015
  getTableName(indexName) {
960
- return this.schema ? `${this.schema}."${indexName}"` : `"${indexName}"`;
1016
+ const parsedIndexName = parseSqlIdentifier(indexName, "table name");
1017
+ const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, "schema name") : void 0;
1018
+ return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
961
1019
  }
962
1020
  async getEvalsByAgentName(agentName, type) {
963
1021
  try {
@@ -1032,12 +1090,14 @@ var PostgresStore = class extends MastraStorage {
1032
1090
  }
1033
1091
  if (attributes) {
1034
1092
  Object.keys(attributes).forEach((key) => {
1035
- conditions.push(`attributes->>'${key}' = $${idx++}`);
1093
+ const parsedKey = parseSqlIdentifier(key, "attribute key");
1094
+ conditions.push(`attributes->>'${parsedKey}' = $${idx++}`);
1036
1095
  });
1037
1096
  }
1038
1097
  if (filters) {
1039
1098
  Object.entries(filters).forEach(([key]) => {
1040
- conditions.push(`${key} = $${idx++}`);
1099
+ const parsedKey = parseSqlIdentifier(key, "filter key");
1100
+ conditions.push(`${parsedKey} = $${idx++}`);
1041
1101
  });
1042
1102
  }
1043
1103
  if (fromDate) {
@@ -1139,10 +1199,11 @@ var PostgresStore = class extends MastraStorage {
1139
1199
  }) {
1140
1200
  try {
1141
1201
  const columns = Object.entries(schema).map(([name, def]) => {
1202
+ const parsedName = parseSqlIdentifier(name, "column name");
1142
1203
  const constraints = [];
1143
1204
  if (def.primaryKey) constraints.push("PRIMARY KEY");
1144
1205
  if (!def.nullable) constraints.push("NOT NULL");
1145
- return `"${name}" ${def.type.toUpperCase()} ${constraints.join(" ")}`;
1206
+ return `"${parsedName}" ${def.type.toUpperCase()} ${constraints.join(" ")}`;
1146
1207
  }).join(",\n");
1147
1208
  if (this.schema) {
1148
1209
  await this.setupSchema();
@@ -1179,7 +1240,7 @@ var PostgresStore = class extends MastraStorage {
1179
1240
  }
1180
1241
  async insert({ tableName, record }) {
1181
1242
  try {
1182
- const columns = Object.keys(record);
1243
+ const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
1183
1244
  const values = Object.values(record);
1184
1245
  const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
1185
1246
  await this.db.none(
@@ -1193,7 +1254,7 @@ var PostgresStore = class extends MastraStorage {
1193
1254
  }
1194
1255
  async load({ tableName, keys }) {
1195
1256
  try {
1196
- const keyEntries = Object.entries(keys);
1257
+ const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
1197
1258
  const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(" AND ");
1198
1259
  const values = keyEntries.map(([_, value]) => value);
1199
1260
  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.3.5-alpha.0",
4
4
  "description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "pg": "^8.13.3",
25
25
  "pg-promise": "^11.11.0",
26
26
  "xxhash-wasm": "^1.1.0",
27
- "@mastra/core": "^0.9.4"
27
+ "@mastra/core": "^0.9.5-alpha.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@microsoft/api-extractor": "^7.52.5",
@@ -16,6 +16,7 @@ import type {
16
16
  WorkflowRun,
17
17
  WorkflowRuns,
18
18
  } from '@mastra/core/storage';
19
+ import { parseSqlIdentifier } from '@mastra/core/utils';
19
20
  import type { WorkflowRunState } from '@mastra/core/workflows';
20
21
  import pgPromise from 'pg-promise';
21
22
  import type { ISSLConfig } from 'pg-promise/typescript/pg-subset';
@@ -93,7 +94,9 @@ export class PostgresStore extends MastraStorage {
93
94
  }
94
95
 
95
96
  private getTableName(indexName: string) {
96
- return this.schema ? `${this.schema}."${indexName}"` : `"${indexName}"`;
97
+ const parsedIndexName = parseSqlIdentifier(indexName, 'table name');
98
+ const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, 'schema name') : undefined;
99
+ return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
97
100
  }
98
101
 
99
102
  async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
@@ -192,13 +195,15 @@ export class PostgresStore extends MastraStorage {
192
195
  }
193
196
  if (attributes) {
194
197
  Object.keys(attributes).forEach(key => {
195
- conditions.push(`attributes->>'${key}' = \$${idx++}`);
198
+ const parsedKey = parseSqlIdentifier(key, 'attribute key');
199
+ conditions.push(`attributes->>'${parsedKey}' = \$${idx++}`);
196
200
  });
197
201
  }
198
202
 
199
203
  if (filters) {
200
204
  Object.entries(filters).forEach(([key]) => {
201
- conditions.push(`${key} = \$${idx++}`);
205
+ const parsedKey = parseSqlIdentifier(key, 'filter key');
206
+ conditions.push(`${parsedKey} = \$${idx++}`);
202
207
  });
203
208
  }
204
209
 
@@ -341,10 +346,11 @@ export class PostgresStore extends MastraStorage {
341
346
  try {
342
347
  const columns = Object.entries(schema)
343
348
  .map(([name, def]) => {
349
+ const parsedName = parseSqlIdentifier(name, 'column name');
344
350
  const constraints = [];
345
351
  if (def.primaryKey) constraints.push('PRIMARY KEY');
346
352
  if (!def.nullable) constraints.push('NOT NULL');
347
- return `"${name}" ${def.type.toUpperCase()} ${constraints.join(' ')}`;
353
+ return `"${parsedName}" ${def.type.toUpperCase()} ${constraints.join(' ')}`;
348
354
  })
349
355
  .join(',\n');
350
356
 
@@ -392,7 +398,7 @@ export class PostgresStore extends MastraStorage {
392
398
 
393
399
  async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
394
400
  try {
395
- const columns = Object.keys(record);
401
+ const columns = Object.keys(record).map(col => parseSqlIdentifier(col, 'column name'));
396
402
  const values = Object.values(record);
397
403
  const placeholders = values.map((_, i) => `$${i + 1}`).join(', ');
398
404
 
@@ -408,7 +414,7 @@ export class PostgresStore extends MastraStorage {
408
414
 
409
415
  async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
410
416
  try {
411
- const keyEntries = Object.entries(keys);
417
+ const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, 'column name'), value]);
412
418
  const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(' AND ');
413
419
  const values = keyEntries.map(([_, value]) => value);
414
420