Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions spec/GridFSBucketStorageAdapter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,14 @@ describe_only_db('mongo')('GridFSBucket', () => {
expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile);
});

it('properly upload a file when disableIndexFieldValidation exist in databaseOptions', async () => {
const gfsAdapter = new GridFSBucketAdapter(databaseURI, { disableIndexFieldValidation: true });
const twoMegabytesFile = randomString(2048 * 1024);
const res = await gfsAdapter.createFile('myFileName', twoMegabytesFile);
expect(res._id).toBeTruthy();
expect(res.filename).toEqual('myFileName');
});

it('properly deletes a file from GridFS', async () => {
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
await gfsAdapter.createFile('myFileName', 'a simple file');
Expand Down
24 changes: 24 additions & 0 deletions spec/schemas.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3008,6 +3008,7 @@ describe('schemas', () => {
beforeEach(async () => {
await TestUtils.destroyAllDataPermanently(false);
await config.database.adapter.performInitialization({ VolatileClassesSchemas: [] });
databaseAdapter.disableIndexFieldValidation = false;
});

it('cannot create index if field does not exist', done => {
Expand Down Expand Up @@ -3036,6 +3037,29 @@ describe('schemas', () => {
});
});

it('can create index if field does not exist with disableIndexFieldValidation true ', async () => {
databaseAdapter.disableIndexFieldValidation = true;
await request({
url: 'http://localhost:8378/1/schemas/NewClass',
method: 'POST',
headers: masterKeyHeaders,
json: true,
body: {},
});
const response = await request({
url: 'http://localhost:8378/1/schemas/NewClass',
method: 'PUT',
headers: masterKeyHeaders,
json: true,
body: {
indexes: {
name1: { aString: 1 },
},
},
});
expect(response.data.indexes.name1).toEqual({ aString: 1 });
});

it('can create index on default field', done => {
request({
url: 'http://localhost:8378/1/schemas/NewClass',
Expand Down
2 changes: 1 addition & 1 deletion src/Adapters/Files/GridFSBucketAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
const defaultMongoOptions = {
};
const _mongoOptions = Object.assign(defaultMongoOptions, mongoOptions);
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS']) {
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS', 'disableIndexFieldValidation']) {
delete _mongoOptions[key];
}
this._mongoOptions = _mongoOptions;
Expand Down
5 changes: 4 additions & 1 deletion src/Adapters/Storage/Mongo/MongoStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export class MongoStorageAdapter implements StorageAdapter {
canSortOnJoinTables: boolean;
enableSchemaHooks: boolean;
schemaCacheTtl: ?number;
disableIndexFieldValidation: boolean;

constructor({ uri = defaults.DefaultMongoURI, collectionPrefix = '', mongoOptions = {} }: any) {
this._uri = uri;
Expand All @@ -152,7 +153,8 @@ export class MongoStorageAdapter implements StorageAdapter {
this.canSortOnJoinTables = true;
this.enableSchemaHooks = !!mongoOptions.enableSchemaHooks;
this.schemaCacheTtl = mongoOptions.schemaCacheTtl;
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS']) {
this.disableIndexFieldValidation = !!mongoOptions.disableIndexFieldValidation;
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS', 'disableIndexFieldValidation']) {
delete mongoOptions[key];
delete this._mongoOptions[key];
}
Expand Down Expand Up @@ -289,6 +291,7 @@ export class MongoStorageAdapter implements StorageAdapter {
} else {
Object.keys(field).forEach(key => {
if (
!this.disableIndexFieldValidation &&
!Object.prototype.hasOwnProperty.call(
fields,
key.indexOf('_p_') === 0 ? key.replace('_p_', '') : key
Expand Down
21 changes: 17 additions & 4 deletions src/Adapters/Storage/Postgres/PostgresStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -862,13 +862,16 @@ export class PostgresStorageAdapter implements StorageAdapter {
_stream: any;
_uuid: any;
schemaCacheTtl: ?number;
disableIndexFieldValidation: boolean;

constructor({ uri, collectionPrefix = '', databaseOptions = {} }: any) {
const options = { ...databaseOptions };
this._collectionPrefix = collectionPrefix;
this.enableSchemaHooks = !!databaseOptions.enableSchemaHooks;
this.disableIndexFieldValidation = !!databaseOptions.disableIndexFieldValidation;

this.schemaCacheTtl = databaseOptions.schemaCacheTtl;
for (const key of ['enableSchemaHooks', 'schemaCacheTtl']) {
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'disableIndexFieldValidation']) {
delete options[key];
}

Expand Down Expand Up @@ -991,7 +994,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
delete existingIndexes[name];
} else {
Object.keys(field).forEach(key => {
if (!Object.prototype.hasOwnProperty.call(fields, key)) {
if (
!this.disableIndexFieldValidation &&
!Object.prototype.hasOwnProperty.call(fields, key)
) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
`Field ${key} does not exist, cannot add index.`
Expand All @@ -1006,8 +1012,15 @@ export class PostgresStorageAdapter implements StorageAdapter {
}
});
await conn.tx('set-indexes-with-schema-format', async t => {
if (insertedIndexes.length > 0) {
await self.createIndexes(className, insertedIndexes, t);
try {
if (insertedIndexes.length > 0) {
await self.createIndexes(className, insertedIndexes, t);
}
} catch (e) {
const columnDoesNotExistError = e.errors && e.errors[0] && e.errors[0].code === '42703';
if (columnDoesNotExistError && !this.disableIndexFieldValidation) {
throw e;
}
}
Comment on lines +1015 to 1024
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t swallow non-42703 errors; only ignore missing-column when flag is enabled

Current catch block suppresses all errors except one case; this can hide genuine failures and still update _SCHEMA, causing drift.

Apply this diff to rethrow by default and use the defined constant:

-      try {
-        if (insertedIndexes.length > 0) {
-          await self.createIndexes(className, insertedIndexes, t);
-        }
-      } catch (e) {
-        const columnDoesNotExistError = e.errors && e.errors[0] && e.errors[0].code === '42703';
-        if (columnDoesNotExistError && !this.disableIndexFieldValidation) {
-          throw e;
-        }
-      }
+      try {
+        if (insertedIndexes.length > 0) {
+          await self.createIndexes(className, insertedIndexes, t);
+        }
+      } catch (e) {
+        const code =
+          e?.code || (e?.errors && e.errors[0] && e.errors[0].code);
+        const shouldSwallow =
+          this.disableIndexFieldValidation && code === PostgresMissingColumnError;
+        if (!shouldSwallow) throw e;
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
if (insertedIndexes.length > 0) {
await self.createIndexes(className, insertedIndexes, t);
}
} catch (e) {
const columnDoesNotExistError = e.errors && e.errors[0] && e.errors[0].code === '42703';
if (columnDoesNotExistError && !this.disableIndexFieldValidation) {
throw e;
}
}
try {
if (insertedIndexes.length > 0) {
await self.createIndexes(className, insertedIndexes, t);
}
} catch (e) {
const code =
e?.code || (e?.errors && e.errors[0] && e.errors[0].code);
const shouldSwallow =
this.disableIndexFieldValidation && code === PostgresMissingColumnError;
if (!shouldSwallow) throw e;
}
🤖 Prompt for AI Agents
In src/Adapters/Storage/Postgres/PostgresStorageAdapter.js around lines
1015-1024, the current catch block swallows all errors except one case; change
it to rethrow by default and only ignore the missing-column error when the
index-field-validation flag allows it. Replace the hard-coded '42703' check with
the existing constant (e.g. POSTGRES_COLUMN_DOES_NOT_EXIST_ERROR_CODE) and
implement logic: detect the column-missing error via that constant, and only
suppress (do not rethrow) when it is a column-missing error AND
this.disableIndexFieldValidation is true; otherwise rethrow the caught error.

if (deletedIndexes.length > 0) {
await self.dropIndexes(className, deletedIndexes, t);
Expand Down
Loading