Skip to content

Commit

Permalink
feat: add concurrent indexes for postgres (#10442)
Browse files Browse the repository at this point in the history
  • Loading branch information
slukes committed Dec 29, 2023
1 parent 75ec8f2 commit f4e6eaf
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 5 deletions.
12 changes: 12 additions & 0 deletions docs/indices.md
Expand Up @@ -137,6 +137,18 @@ export class Thing {
}
```

## Concurrent creation

In order to avoid having to obtain an access exclusive lock when creating and dropping indexes in postgres, you may create them using the CONCURRENTLY modifier.

Typeorm supports generating SQL with this option if when the concurrent option is specified on the index.

```typescript
@Index(["firstName", "middleName", "lastName"], { concurrent: true })
```

For more information see the [postgres documentation](https://www.postgresql.org/docs/current/sql-createindex.html).

## Disabling synchronization

TypeORM does not support some index options and definitions (e.g. `lower`, `pg_trgm`) because of lot of different database specifics and multiple
Expand Down
1 change: 1 addition & 0 deletions src/decorator/Index.ts
Expand Up @@ -139,6 +139,7 @@ export function Index(
parser: options ? options.parser : undefined,
sparse: options && options.sparse ? true : false,
background: options && options.background ? true : false,
concurrent: options && options.concurrent ? true : false,
expireAfterSeconds: options
? options.expireAfterSeconds
: undefined,
Expand Down
6 changes: 6 additions & 0 deletions src/decorator/options/IndexOptions.ts
Expand Up @@ -52,6 +52,12 @@ export interface IndexOptions {
*/
background?: boolean

/**
* Create the index using the CONCURRENTLY modifier
* Works only in postgres.
*/
concurrent?: boolean

/**
* Specifies a time to live, in seconds.
* This option is only supported for mongodb database.
Expand Down
21 changes: 16 additions & 5 deletions src/driver/postgres/PostgresQueryRunner.ts
Expand Up @@ -4257,9 +4257,9 @@ export class PostgresQueryRunner
.map((columnName) => `"${columnName}"`)
.join(", ")
return new Query(
`CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
index.name
}" ON ${this.escapePath(table)} ${
`CREATE ${index.isUnique ? "UNIQUE " : ""}${
index.isConcurrent ? "CONCURRENTLY " : ""
}INDEX "${index.name}" ON ${this.escapePath(table)} ${
index.isSpatial ? "USING GiST " : ""
}(${columns}) ${index.where ? "WHERE " + index.where : ""}`,
)
Expand Down Expand Up @@ -4291,10 +4291,21 @@ export class PostgresQueryRunner
let indexName = InstanceChecker.isTableIndex(indexOrName)
? indexOrName.name
: indexOrName
const concurrent = InstanceChecker.isTableIndex(indexOrName)
? indexOrName.isConcurrent
: false
const { schema } = this.driver.parseTableName(table)
return schema
? new Query(`DROP INDEX "${schema}"."${indexName}"`)
: new Query(`DROP INDEX "${indexName}"`)
? new Query(
`DROP INDEX ${
concurrent ? "CONCURRENTLY" : ""
}"${schema}"."${indexName}"`,
)
: new Query(
`DROP INDEX ${
concurrent ? "CONCURRENTLY" : ""
}"${indexName}"`,
)
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/metadata-args/IndexMetadataArgs.ts
Expand Up @@ -72,6 +72,12 @@ export interface IndexMetadataArgs {
*/
background?: boolean

/**
* Builds the index using the concurrently option.
* This option is only supported for postgres database.
*/
concurrent?: boolean

/**
* Specifies a time to live, in seconds.
* This option is only supported for mongodb database.
Expand Down
7 changes: 7 additions & 0 deletions src/metadata/IndexMetadata.ts
Expand Up @@ -73,6 +73,12 @@ export class IndexMetadata {
*/
isBackground?: boolean

/**
* Builds the index using the concurrently option.
* This options is only supported for postgres database.
*/
isConcurrent?: boolean

/**
* Specifies a time to live, in seconds.
* This option is only supported for mongodb database.
Expand Down Expand Up @@ -148,6 +154,7 @@ export class IndexMetadata {
this.where = options.args.where
this.isSparse = options.args.sparse
this.isBackground = options.args.background
this.isConcurrent = options.args.concurrent
this.expireAfterSeconds = options.args.expireAfterSeconds
this.givenName = options.args.name
this.givenColumnNames = options.args.columns
Expand Down
6 changes: 6 additions & 0 deletions src/schema-builder/options/TableIndexOptions.ts
Expand Up @@ -27,6 +27,12 @@ export interface TableIndexOptions {
*/
isSpatial?: boolean

/**
* Builds the index using the concurrently option.
* This options is only supported for postgres database.
*/
isConcurrent?: boolean

/**
* The FULLTEXT modifier indexes the entire column and does not allow prefixing.
* Supported only in MySQL & SAP HANA.
Expand Down
9 changes: 9 additions & 0 deletions src/schema-builder/table/TableIndex.ts
Expand Up @@ -32,6 +32,12 @@ export class TableIndex {
*/
isSpatial: boolean

/**
* Create the index using the CONCURRENTLY modifier
* Works only in postgres.
*/
isConcurrent: boolean

/**
* The FULLTEXT modifier indexes the entire column and does not allow prefixing.
* Works only in MySQL.
Expand Down Expand Up @@ -67,6 +73,7 @@ export class TableIndex {
this.columnNames = options.columnNames
this.isUnique = !!options.isUnique
this.isSpatial = !!options.isSpatial
this.isConcurrent = !!options.isConcurrent
this.isFulltext = !!options.isFulltext
this.isNullFiltered = !!options.isNullFiltered
this.parser = options.parser
Expand All @@ -86,6 +93,7 @@ export class TableIndex {
columnNames: [...this.columnNames],
isUnique: this.isUnique,
isSpatial: this.isSpatial,
isConcurrent: this.isConcurrent,
isFulltext: this.isFulltext,
isNullFiltered: this.isNullFiltered,
parser: this.parser,
Expand All @@ -108,6 +116,7 @@ export class TableIndex {
),
isUnique: indexMetadata.isUnique,
isSpatial: indexMetadata.isSpatial,
isConcurrent: indexMetadata.isConcurrent,
isFulltext: indexMetadata.isFulltext,
isNullFiltered: indexMetadata.isNullFiltered,
parser: indexMetadata.parser,
Expand Down

0 comments on commit f4e6eaf

Please sign in to comment.