Skip to content
Open
Show file tree
Hide file tree
Changes from all 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);
await gfsAdapter.createFile('myFileName', twoMegabytesFile);
const gfsResult = await gfsAdapter.getFileData('myFileName');
expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile);
});

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