UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

84 lines (80 loc) 4.04 kB
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]); } }