Skip to content

Commit

Permalink
feat: support time travel queries, upsert, enums, spatial types in co…
Browse files Browse the repository at this point in the history
…ckroachdb (#9128)

* feature: adds support for enum type (fixes #9068)

* temporarily ran package to test on different repo

* playing around - bumped version

* Revert "playing around - bumped version"

This reverts commit 7df4adb.

* Revert "temporarily ran package to test on different repo"

This reverts commit 48f394e.

* feat: add support for geometry data type

* feature: properly hydrate enum array values

* feature: adds support for geography and geometry for cockroachdb

* bugfix: fixes issue with primary generated columns being invalid column type (fixes #8532)

* Revert "bugfix: fixes issue with primary generated columns being invalid column type (fixes #8532)"

This reverts commit e00cdb0.

* bugfix: type casts to string when using ANY

* feature: cast geometry/geography to geojson

* feature: added references to srid

* bugfix: prevent error if trying to close already closed connection

* feature: added cockrachodb as part of postgres family

* feature: ensures support for spatial columns for cockroachdb

* feature: adds support for UPSERT for CockroachDB (fixes #9199)

* minor: added TODO; unsure how to achieve this

* feature: adds support for time travelling queries for cockroachdb

* bugfix: only run time travel query on SELECT statements

* refactor: changed UsertType from 'upsert' to 'primary-key' since this is more logical

* feature: added posibility to set timeTravelQuery to false, instead of the parameter function; help for disabling time travel queries during tests

* feature: allow timeTravelQueries in find* queries

* bugfix: when using timetravel queries with joinAttributes it now prevents error 'AS OF SYSTEM TIME must be in top level' error

* lint

* minor fix

* fixed failing test

* implemented ENUM type;
added tests;

* fixed failing tests

* fixed failing test

* fixed spatial types synchronization;
implemented spatial indices;
added tests for spatial columns;

* refactored Time Travel Query functionality;
removed TTQ from find options;
added tests for TTQ;

* added docs for Time Travel Queries

* minor changes

* added GeoJSON types;
other minor fixes;

* updated docs

* updated docs

Co-authored-by: Dmitry Zotov <dmzt08@gmail.com>
  • Loading branch information
simplenotezy and AlexMesser committed Jan 3, 2023
1 parent 3e1caf0 commit defb409
Show file tree
Hide file tree
Showing 53 changed files with 2,555 additions and 297 deletions.
4 changes: 2 additions & 2 deletions docs/decorator-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ export class User {
- `hstoreType: "object"|"string"` - Return type of `HSTORE` column. Returns value as string or as object. Used only in [Postgres](https://www.postgresql.org/docs/9.6/static/hstore.html).
- `array: boolean` - Used for postgres and cockroachdb column types which can be array (for example int[]).
- `transformer: ValueTransformer|ValueTransformer[]` - Specifies a value transformer (or array of value transformers) that is to be used to (un)marshal this column when reading or writing to the database. In case of an array, the value transformers will be applied in the natural order from entityValue to databaseValue, and in reverse order from databaseValue to entityValue.
- `spatialFeatureType: string` - Optional feature type (`Point`, `Polygon`, `LineString`, `Geometry`) used as a constraint on a spatial column. If not specified, it will behave as though `Geometry` was provided. Used only in PostgreSQL.
- `srid: number` - Optional [Spatial Reference ID](https://postgis.net/docs/using_postgis_dbmanagement.html#spatial_ref_sys) used as a constraint on a spatial column. If not specified, it will default to `0`. Standard geographic coordinates (latitude/longitude in the WGS84 datum) correspond to [EPSG 4326](http://spatialreference.org/ref/epsg/wgs-84/). Used only in PostgreSQL.
- `spatialFeatureType: string` - Optional feature type (`Point`, `Polygon`, `LineString`, `Geometry`) used as a constraint on a spatial column. If not specified, it will behave as though `Geometry` was provided. Used only in PostgreSQL and CockroachDB.
- `srid: number` - Optional [Spatial Reference ID](https://postgis.net/docs/using_postgis_dbmanagement.html#spatial_ref_sys) used as a constraint on a spatial column. If not specified, it will default to `0`. Standard geographic coordinates (latitude/longitude in the WGS84 datum) correspond to [EPSG 4326](http://spatialreference.org/ref/epsg/wgs-84/). Used only in PostgreSQL and CockroachDB.

Learn more about [entity columns](entities.md#entity-columns).

Expand Down
85 changes: 81 additions & 4 deletions docs/entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ There are several special column types with additional functionality available:

### Spatial columns

MS SQL, MySQL / MariaDB, and PostgreSQL all support spatial columns. TypeORM's
MS SQL, MySQL, MariaDB, PostgreSQL and CockroachDB all support spatial columns. TypeORM's
support for each varies slightly between databases, particularly as the column
names vary between databases.

Expand All @@ -207,10 +207,85 @@ be provided as [well-known text
(WKT)](https://en.wikipedia.org/wiki/Well-known_text), so geometry columns
should be tagged with the `string` type.

TypeORM's PostgreSQL support uses [GeoJSON](http://geojson.org/) as an
```typescript
import { Entity, PrimaryColumn, Column } from "typeorm"

@Entity()
export class Thing {
@PrimaryColumn()
id: number

@Column("point")
point: string

@Column("linestring")
linestring: string
}

...

const thing = new Thing()
thing.point = "POINT(1 1)"
thing.linestring = "LINESTRING(0 0,1 1,2 2)"
```

TypeORM's PostgreSQL and CockroachDB support uses [GeoJSON](http://geojson.org/) as an
interchange format, so geometry columns should be tagged either as `object` or
`Geometry` (or subclasses, e.g. `Point`) after importing [`geojson`
types](https://www.npmjs.com/package/@types/geojson).
types](https://www.npmjs.com/package/@types/geojson) or using TypeORM built in [GeoJSON types](../src/driver/types/GeoJsonTypes.ts).

```typescript
import {
Entity,
PrimaryColumn,
Column,
Point,
LineString,
MultiPoint
} from "typeorm"

@Entity()
export class Thing {
@PrimaryColumn()
id: number

@Column("geometry")
point: Point

@Column("geometry")
linestring: LineString

@Column("geometry", {
spatialFeatureType: "MultiPoint",
srid: 4326,
})
multiPointWithSRID: MultiPoint
}

...

const thing = new Thing()
thing.point = {
type: "Point",
coordinates: [116.443987, 39.920843],
}
thing.linestring = {
type: "LineString",
coordinates: [
[-87.623177, 41.881832],
[-90.199402, 38.627003],
[-82.446732, 38.413651],
[-87.623177, 41.881832],
],
}
thing.multiPointWithSRID = {
type: "MultiPoint",
coordinates: [
[100.0, 0.0],
[101.0, 1.0],
],
}
```

TypeORM tries to do the right thing, but it's not always possible to determine
when a value being inserted or the result of a PostGIS function should be
Expand All @@ -219,7 +294,9 @@ to the following, where values are converted into PostGIS `geometry`s from
GeoJSON and into GeoJSON as `json`:

```typescript
const origin = {
import { Point } from "typeorm"

const origin: Point = {
type: "Point",
coordinates: [0, 0],
}
Expand Down
4 changes: 2 additions & 2 deletions docs/indices.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class User {

## Spatial Indices

MySQL and PostgreSQL (when PostGIS is available) both support spatial indices.
MySQL, CockroachDB and PostgreSQL (when PostGIS is available) supports spatial indices.

To create a spatial index on a column in MySQL, add an `Index` with `spatial: true` on a column that uses a spatial type (`geometry`, `point`, `linestring`,
`polygon`, `multipoint`, `multilinestring`, `multipolygon`,
Expand All @@ -118,7 +118,7 @@ export class Thing {
}
```

To create a spatial index on a column in PostgreSQL, add an `Index` with `spatial: true` on a column that uses a spatial type (`geometry`, `geography`):
To create a spatial index on a column add an `Index` with `spatial: true` on a column that uses a spatial type (`geometry`, `geography`):

```typescript
export interface Geometry {
Expand Down
3 changes: 2 additions & 1 deletion docs/repository-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ await repository.upsert(
{
conflictPaths: ["externalId"],
skipUpdateIfNoValuesChanged: true, // supported by postgres, skips update if it would not change row values
upsertType: "upsert", // "on-conflict-do-update" | "on-duplicate-key-update" | "upsert" - optionally provide an UpsertType - 'upsert' is currently only supported by CockroachDB
},
)
/** executes
Expand Down Expand Up @@ -211,7 +212,7 @@ await repository.upsert(
* ON CONFLICT (externalId) WHERE ( dateAdded > 2021-01-01 ) DO UPDATE
* SET firstName = EXCLUDED.firstName,
* SET dateAdded = EXCLUDED.dateAdded,
* WHERE user.firstName IS DISTINCT FROM EXCLUDED.firstName OR user.dateAdded IS DISTINCT FROM EXCLUDED.dateAdded
* WHERE user.firstName IS DISTINCT FROM EXCLUDED.firstName OR user.dateAdded IS DISTINCT FROM EXCLUDED.dateAdded
**/
```

Expand Down
35 changes: 35 additions & 0 deletions docs/select-query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -1238,3 +1238,38 @@ const users = await connection.getRepository(User)
.where(`user.id IN (SELECT "id" FROM 'insert_results')`)
.getMany();
```

## Time Travel Queries

[Time Travel Queries](https://www.cockroachlabs.com/blog/time-travel-queries-select-witty_subtitle-the_future/)
currently supported only in `CockroachDB` database.

```typescript
const repository = connection.getRepository(Account)

// create a new account
const account = new Account()
account.name = "John Smith"
account.balance = 100
await repository.save(account)

// imagine we update the account balance 1 hour after creation
account.balance = 200
await repository.save(account)

// outputs { name: "John Smith", balance: "200" }
console.log(account)

// load account state on 1 hour back
account = await repository
.createQueryBuilder("account")
.timeTravelQuery(`'-1h'`)
.getOneOrFail()

// outputs { name: "John Smith", balance: "100" }
console.log(account)
```

By default `timeTravelQuery()` uses `follower_read_timestamp()` function if no arguments passed.
For another supported timestamp arguments and additional information please refer to
[CockroachDB](https://www.cockroachlabs.com/docs/stable/as-of-system-time.html) docs.
4 changes: 3 additions & 1 deletion src/decorator/options/SpatialColumnOptions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Geometry } from "../../driver/types/GeoJsonTypes"

/**
* Options for spatial columns.
*/
Expand All @@ -6,7 +8,7 @@ export interface SpatialColumnOptions {
* Column type's feature type.
* Geometry, Point, Polygon, etc.
*/
spatialFeatureType?: string
spatialFeatureType?: Geometry["type"]

/**
* Column type's SRID.
Expand Down
2 changes: 1 addition & 1 deletion src/driver/Driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export interface Driver {
/**
* Returns type of upsert supported by driver if any
*/
supportedUpsertType?: UpsertType
supportedUpsertTypes: UpsertType[]

/**
* Default values of length, precision and scale depends on column data type.
Expand Down
4 changes: 3 additions & 1 deletion src/driver/DriverUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ export class DriverUtils {
}

static isPostgresFamily(driver: Driver): boolean {
return ["postgres", "aurora-postgres"].includes(driver.options.type)
return ["postgres", "aurora-postgres", "cockroachdb"].includes(
driver.options.type,
)
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/driver/aurora-mysql/AuroraMysqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { Table } from "../../schema-builder/table/Table"
import { View } from "../../schema-builder/view/View"
import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
import { InstanceChecker } from "../../util/InstanceChecker"
import { UpsertType } from "../types/UpsertType"

/**
* Organizes communication with MySQL DBMS.
Expand Down Expand Up @@ -152,7 +153,7 @@ export class AuroraMysqlDriver implements Driver {
/**
* Returns type of upsert supported by driver if any
*/
readonly supportedUpsertType = "on-duplicate-key-update"
supportedUpsertTypes: UpsertType[] = ["on-duplicate-key-update"]

/**
* Gets list of spatial column data types.
Expand Down
6 changes: 6 additions & 0 deletions src/driver/cockroachdb/CockroachConnectionOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export interface CockroachConnectionOptions
*/
readonly type: "cockroachdb"

/**
* Enable time travel queries on cockroachdb.
* https://www.cockroachlabs.com/docs/stable/as-of-system-time.html
*/
readonly timeTravelQueries: boolean

/**
* Schema name.
*/
Expand Down

0 comments on commit defb409

Please sign in to comment.