Skip to content

Commit 1b23475

Browse files
authored
feat: Disable index-field validation to create index for fields that don't yet exist (#8137)
1 parent f024d53 commit 1b23475

File tree

5 files changed

+69
-21
lines changed

5 files changed

+69
-21
lines changed

spec/GridFSBucketStorageAdapter.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,14 @@ describe_only_db('mongo')('GridFSBucket', () => {
421421
expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile);
422422
});
423423

424+
it('properly upload a file when disableIndexFieldValidation exist in databaseOptions', async () => {
425+
const gfsAdapter = new GridFSBucketAdapter(databaseURI, { disableIndexFieldValidation: true });
426+
const twoMegabytesFile = randomString(2048 * 1024);
427+
await gfsAdapter.createFile('myFileName', twoMegabytesFile);
428+
const gfsResult = await gfsAdapter.getFileData('myFileName');
429+
expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile);
430+
});
431+
424432
it('properly deletes a file from GridFS', async () => {
425433
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
426434
await gfsAdapter.createFile('myFileName', 'a simple file');

spec/schemas.spec.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3008,6 +3008,7 @@ describe('schemas', () => {
30083008
beforeEach(async () => {
30093009
await TestUtils.destroyAllDataPermanently(false);
30103010
await config.database.adapter.performInitialization({ VolatileClassesSchemas: [] });
3011+
databaseAdapter.disableIndexFieldValidation = false;
30113012
});
30123013

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

3040+
it('can create index if field does not exist with disableIndexFieldValidation true ', async () => {
3041+
databaseAdapter.disableIndexFieldValidation = true;
3042+
await request({
3043+
url: 'http://localhost:8378/1/schemas/NewClass',
3044+
method: 'POST',
3045+
headers: masterKeyHeaders,
3046+
json: true,
3047+
body: {},
3048+
});
3049+
const response = await request({
3050+
url: 'http://localhost:8378/1/schemas/NewClass',
3051+
method: 'PUT',
3052+
headers: masterKeyHeaders,
3053+
json: true,
3054+
body: {
3055+
indexes: {
3056+
name1: { aString: 1 },
3057+
},
3058+
},
3059+
});
3060+
expect(response.data.indexes.name1).toEqual({ aString: 1 });
3061+
});
3062+
30393063
it('can create index on default field', done => {
30403064
request({
30413065
url: 'http://localhost:8378/1/schemas/NewClass',

src/Adapters/Files/GridFSBucketAdapter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
3737
const defaultMongoOptions = {
3838
};
3939
const _mongoOptions = Object.assign(defaultMongoOptions, mongoOptions);
40-
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS']) {
40+
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS', 'disableIndexFieldValidation']) {
4141
delete _mongoOptions[key];
4242
}
4343
this._mongoOptions = _mongoOptions;

src/Adapters/Storage/Mongo/MongoStorageAdapter.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export class MongoStorageAdapter implements StorageAdapter {
140140
canSortOnJoinTables: boolean;
141141
enableSchemaHooks: boolean;
142142
schemaCacheTtl: ?number;
143+
disableIndexFieldValidation: boolean;
143144

144145
constructor({ uri = defaults.DefaultMongoURI, collectionPrefix = '', mongoOptions = {} }: any) {
145146
this._uri = uri;
@@ -152,7 +153,8 @@ export class MongoStorageAdapter implements StorageAdapter {
152153
this.canSortOnJoinTables = true;
153154
this.enableSchemaHooks = !!mongoOptions.enableSchemaHooks;
154155
this.schemaCacheTtl = mongoOptions.schemaCacheTtl;
155-
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS']) {
156+
this.disableIndexFieldValidation = !!mongoOptions.disableIndexFieldValidation;
157+
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'maxTimeMS', 'disableIndexFieldValidation']) {
156158
delete mongoOptions[key];
157159
delete this._mongoOptions[key];
158160
}
@@ -289,6 +291,7 @@ export class MongoStorageAdapter implements StorageAdapter {
289291
} else {
290292
Object.keys(field).forEach(key => {
291293
if (
294+
!this.disableIndexFieldValidation &&
292295
!Object.prototype.hasOwnProperty.call(
293296
fields,
294297
key.indexOf('_p_') === 0 ? key.replace('_p_', '') : key

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -627,13 +627,11 @@ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClaus
627627
const distance = fieldValue.$maxDistance;
628628
const distanceInKM = distance * 6371 * 1000;
629629
patterns.push(
630-
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${
631-
index + 2
630+
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${index + 2
632631
})::geometry) <= $${index + 3}`
633632
);
634633
sorts.push(
635-
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${
636-
index + 2
634+
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${index + 2
637635
})::geometry) ASC`
638636
);
639637
values.push(fieldName, point.longitude, point.latitude, distanceInKM);
@@ -681,8 +679,7 @@ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClaus
681679
}
682680
const distanceInKM = distance * 6371 * 1000;
683681
patterns.push(
684-
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${
685-
index + 2
682+
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${index + 2
686683
})::geometry) <= $${index + 3}`
687684
);
688685
values.push(fieldName, point.longitude, point.latitude, distanceInKM);
@@ -862,19 +859,22 @@ export class PostgresStorageAdapter implements StorageAdapter {
862859
_stream: any;
863860
_uuid: any;
864861
schemaCacheTtl: ?number;
862+
disableIndexFieldValidation: boolean;
865863

866864
constructor({ uri, collectionPrefix = '', databaseOptions = {} }: any) {
867865
const options = { ...databaseOptions };
868866
this._collectionPrefix = collectionPrefix;
869867
this.enableSchemaHooks = !!databaseOptions.enableSchemaHooks;
868+
this.disableIndexFieldValidation = !!databaseOptions.disableIndexFieldValidation;
869+
870870
this.schemaCacheTtl = databaseOptions.schemaCacheTtl;
871-
for (const key of ['enableSchemaHooks', 'schemaCacheTtl']) {
871+
for (const key of ['enableSchemaHooks', 'schemaCacheTtl', 'disableIndexFieldValidation']) {
872872
delete options[key];
873873
}
874874

875875
const { client, pgp } = createClient(uri, options);
876876
this._client = client;
877-
this._onchange = () => {};
877+
this._onchange = () => { };
878878
this._pgp = pgp;
879879
this._uuid = uuidv4();
880880
this.canSortOnJoinTables = false;
@@ -991,7 +991,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
991991
delete existingIndexes[name];
992992
} else {
993993
Object.keys(field).forEach(key => {
994-
if (!Object.prototype.hasOwnProperty.call(fields, key)) {
994+
if (
995+
!this.disableIndexFieldValidation &&
996+
!Object.prototype.hasOwnProperty.call(fields, key)
997+
) {
995998
throw new Parse.Error(
996999
Parse.Error.INVALID_QUERY,
9971000
`Field ${key} does not exist, cannot add index.`
@@ -1006,8 +1009,22 @@ export class PostgresStorageAdapter implements StorageAdapter {
10061009
}
10071010
});
10081011
await conn.tx('set-indexes-with-schema-format', async t => {
1009-
if (insertedIndexes.length > 0) {
1010-
await self.createIndexes(className, insertedIndexes, t);
1012+
try {
1013+
if (insertedIndexes.length > 0) {
1014+
await self.createIndexes(className, insertedIndexes, t);
1015+
}
1016+
} catch (e) {
1017+
// pg-promise use Batch error see https://github.com/vitaly-t/spex/blob/e572030f261be1a8e9341fc6f637e36ad07f5231/src/errors/batch.js#L59
1018+
const columnDoesNotExistError = e.getErrors && e.getErrors()[0] && e.getErrors()[0].code === '42703';
1019+
// Specific case when the column does not exist
1020+
if (columnDoesNotExistError) {
1021+
// If the disableIndexFieldValidation is true, we should ignore the error
1022+
if (!this.disableIndexFieldValidation) {
1023+
throw e;
1024+
}
1025+
} else {
1026+
throw e;
1027+
}
10111028
}
10121029
if (deletedIndexes.length > 0) {
10131030
await self.dropIndexes(className, deletedIndexes, t);
@@ -1625,16 +1642,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
16251642
index += 2;
16261643
} else if (fieldValue.__op === 'Remove') {
16271644
updatePatterns.push(
1628-
`$${index}:name = array_remove(COALESCE($${index}:name, '[]'::jsonb), $${
1629-
index + 1
1645+
`$${index}:name = array_remove(COALESCE($${index}:name, '[]'::jsonb), $${index + 1
16301646
}::jsonb)`
16311647
);
16321648
values.push(fieldName, JSON.stringify(fieldValue.objects));
16331649
index += 2;
16341650
} else if (fieldValue.__op === 'AddUnique') {
16351651
updatePatterns.push(
1636-
`$${index}:name = array_add_unique(COALESCE($${index}:name, '[]'::jsonb), $${
1637-
index + 1
1652+
`$${index}:name = array_add_unique(COALESCE($${index}:name, '[]'::jsonb), $${index + 1
16381653
}::jsonb)`
16391654
);
16401655
values.push(fieldName, JSON.stringify(fieldValue.objects));
@@ -1745,8 +1760,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
17451760
updateObject = `COALESCE($${index}:name, '{}'::jsonb)`;
17461761
}
17471762
updatePatterns.push(
1748-
`$${index}:name = (${updateObject} ${deletePatterns} ${incrementPatterns} || $${
1749-
index + 1 + keysToDelete.length
1763+
`$${index}:name = (${updateObject} ${deletePatterns} ${incrementPatterns} || $${index + 1 + keysToDelete.length
17501764
}::jsonb )`
17511765
);
17521766
values.push(fieldName, ...keysToDelete, JSON.stringify(fieldValue));
@@ -2185,8 +2199,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
21852199
groupByFields.push(`"${source}"`);
21862200
}
21872201
columns.push(
2188-
`EXTRACT(${
2189-
mongoAggregateToPostgres[operation]
2202+
`EXTRACT(${mongoAggregateToPostgres[operation]
21902203
} FROM $${index}:name AT TIME ZONE 'UTC')::integer AS $${index + 1}:name`
21912204
);
21922205
values.push(source, alias);

0 commit comments

Comments
 (0)