@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
84 lines (80 loc) • 4.04 kB
JavaScript
import assert from 'node:assert';
import { useEnv } from '@directus/env';
import { toArray } from '@directus/utils';
import {} from 'knex';
import { SchemaHelper } from '../types.js';
const env = useEnv();
export class SchemaHelperCockroachDb extends SchemaHelper {
async changeToType(table, column, type, options = {}) {
await this.changeToTypeByCopy(table, column, type, options);
}
constraintName(existingName) {
const suffix = '_replaced';
// CockroachDB does not allow for dropping/creating constraints with the same
// name in a single transaction. reference issue #14873
if (existingName.endsWith(suffix)) {
return existingName.substring(0, existingName.length - suffix.length);
}
else {
return existingName + suffix;
}
}
async changePrimaryKey(table, to) {
const primaryColumns = toArray(to);
const placeholders = primaryColumns.map(() => '??').join(', ');
assert(primaryColumns.length > 0, 'At least 1 "to" column is required');
assert(primaryColumns[0] && primaryColumns[0].length > 0, '"to" column cannot be empty');
/* Before adding the new PK field(s) we drop the existing constraint to ensure no leftover secondary index on the original PK field.
* - https://www.cockroachlabs.com/docs/stable/primary-key#changing-primary-key-columns
*
* CockroachDB requires that when changing a PK the current one be dropped and new one added in the same transaction.
* To prevent this error from being thrown both operations are executed in the same statement via `,`.
* As it is done in a single operation no need for transaction rollback support.
* - https://www.cockroachlabs.com/docs/stable/primary-key.html
* - https://www.cockroachlabs.com/docs/stable/alter-table#synopsis
*/
await this.knex.raw(`ALTER TABLE ?? DROP CONSTRAINT ?? , ADD CONSTRAINT ?? PRIMARY KEY (${placeholders})`, [
table,
`${table}_pkey`,
`${table}_pkey`,
...primaryColumns,
]);
}
async getDatabaseSize() {
try {
const result = await this.knex
.select(this.knex.raw('round(SUM(range_size_mb) * 1024 * 1024, 0) AS size'))
.from(this.knex.raw('[SHOW RANGES FROM database ??]', [env['DB_DATABASE']]));
return result[0]?.['size'] ? Number(result[0]?.['size']) : null;
}
catch {
return null;
}
}
addInnerSortFieldsToGroupBy(groupByFields, sortRecords, hasRelationalSort) {
if (hasRelationalSort) {
/*
Cockroach allows aliases to be used in the GROUP BY clause and only needs columns in the GROUP BY clause that
are not functionally dependent on the primary key.
> You can group columns by an alias (i.e., a label assigned to the column with an AS clause) rather than the column name.
> If aggregate groups are created on a full primary key, any column in the table can be selected as a target_elem,
or specified in a HAVING clause.
https://www.cockroachlabs.com/docs/stable/select-clause#parameters
*/
groupByFields.push(...sortRecords.map(({ alias }) => alias));
}
}
async createIndex(collection, field, options = {}) {
const isUnique = Boolean(options.unique);
const constraintName = this.generateIndexName(isUnique ? 'unique' : 'index', collection, field);
// https://www.cockroachlabs.com/docs/stable/create-index
if (options.attemptConcurrentIndex) {
return this.knex.raw(`CREATE ${isUnique ? 'UNIQUE ' : ''}INDEX CONCURRENTLY ?? ON ?? (??)`, [
constraintName,
collection,
field,
]);
}
return this.knex.raw(`CREATE ${isUnique ? 'UNIQUE ' : ''}INDEX ?? ON ?? (??)`, [constraintName, collection, field]);
}
}