Wrap postgres class creation in a transaction (#2958)
This commit is contained in:
committed by
Florent Vilmart
parent
0e78c28146
commit
22c790f23c
@@ -4,6 +4,7 @@ const PostgresRelationDoesNotExistError = '42P01';
|
|||||||
const PostgresDuplicateRelationError = '42P07';
|
const PostgresDuplicateRelationError = '42P07';
|
||||||
const PostgresDuplicateColumnError = '42701';
|
const PostgresDuplicateColumnError = '42701';
|
||||||
const PostgresUniqueIndexViolationError = '23505';
|
const PostgresUniqueIndexViolationError = '23505';
|
||||||
|
const PostgresTransactionAbortedError = '25P02';
|
||||||
const logger = require('../../../logger');
|
const logger = require('../../../logger');
|
||||||
|
|
||||||
const debug = function(){
|
const debug = function(){
|
||||||
@@ -385,8 +386,9 @@ export class PostgresStorageAdapter {
|
|||||||
this._client = createClient(uri, databaseOptions);
|
this._client = createClient(uri, databaseOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ensureSchemaCollectionExists() {
|
_ensureSchemaCollectionExists(conn) {
|
||||||
return this._client.none('CREATE TABLE "_SCHEMA" ( "className" varChar(120), "schema" jsonb, "isParseClass" bool, PRIMARY KEY ("className") )')
|
conn = conn || this._client;
|
||||||
|
return conn.none('CREATE TABLE IF NOT EXISTS "_SCHEMA" ( "className" varChar(120), "schema" jsonb, "isParseClass" bool, PRIMARY KEY ("className") )')
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.code === PostgresDuplicateRelationError || error.code === PostgresUniqueIndexViolationError) {
|
if (error.code === PostgresDuplicateRelationError || error.code === PostgresUniqueIndexViolationError) {
|
||||||
// Table already exists, must have been created by a different request. Ignore error.
|
// Table already exists, must have been created by a different request. Ignore error.
|
||||||
@@ -410,13 +412,20 @@ export class PostgresStorageAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createClass(className, schema) {
|
createClass(className, schema) {
|
||||||
return this.createTable(className, schema).then(() => {
|
return this._client.tx(t => {
|
||||||
return this._client.none('INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)', { className, schema });
|
const q1 = this.createTable(className, schema, t);
|
||||||
|
const q2 = t.none('INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)', { className, schema });
|
||||||
|
|
||||||
|
return t.batch([q1, q2]);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return toParseSchema(schema)
|
return toParseSchema(schema)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
if (Array.isArray(err.data) && err.data.length > 1 && err.data[0].result.code === PostgresTransactionAbortedError) {
|
||||||
|
err = err.data[1].result;
|
||||||
|
}
|
||||||
|
|
||||||
if (err.code === PostgresUniqueIndexViolationError && err.detail.includes(className)) {
|
if (err.code === PostgresUniqueIndexViolationError && err.detail.includes(className)) {
|
||||||
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, `Class ${className} already exists.`)
|
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, `Class ${className} already exists.`)
|
||||||
}
|
}
|
||||||
@@ -425,7 +434,8 @@ export class PostgresStorageAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Just create a table, do not insert in schema
|
// Just create a table, do not insert in schema
|
||||||
createTable(className, schema) {
|
createTable(className, schema, conn) {
|
||||||
|
conn = conn || this._client;
|
||||||
debug('createTable', className, schema);
|
debug('createTable', className, schema);
|
||||||
let valuesArray = [];
|
let valuesArray = [];
|
||||||
let patternsArray = [];
|
let patternsArray = [];
|
||||||
@@ -458,10 +468,10 @@ export class PostgresStorageAdapter {
|
|||||||
}
|
}
|
||||||
index = index+2;
|
index = index+2;
|
||||||
});
|
});
|
||||||
const qs = `CREATE TABLE $1:name (${patternsArray.join(',')})`;
|
const qs = `CREATE TABLE IF NOT EXISTS $1:name (${patternsArray.join(',')})`;
|
||||||
const values = [className, ...valuesArray];
|
const values = [className, ...valuesArray];
|
||||||
return this._ensureSchemaCollectionExists()
|
return this._ensureSchemaCollectionExists(conn)
|
||||||
.then(() => this._client.none(qs, values))
|
.then(() => conn.none(qs, values))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.code === PostgresDuplicateRelationError) {
|
if (error.code === PostgresDuplicateRelationError) {
|
||||||
// Table already exists, must have been created by a different request. Ignore error.
|
// Table already exists, must have been created by a different request. Ignore error.
|
||||||
@@ -471,7 +481,7 @@ export class PostgresStorageAdapter {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Create the relation tables
|
// Create the relation tables
|
||||||
return Promise.all(relations.map((fieldName) => {
|
return Promise.all(relations.map((fieldName) => {
|
||||||
return this._client.none('CREATE TABLE IF NOT EXISTS $<joinTable:name> ("relatedId" varChar(120), "owningId" varChar(120), PRIMARY KEY("relatedId", "owningId") )', {joinTable: `_Join:${fieldName}:${className}`});
|
return conn.none('CREATE TABLE IF NOT EXISTS $<joinTable:name> ("relatedId" varChar(120), "owningId" varChar(120), PRIMARY KEY("relatedId", "owningId") )', {joinTable: `_Join:${fieldName}:${className}`});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user