Skip to content

Commit

Permalink
Change text, varchar types, remove char (#277)
Browse files Browse the repository at this point in the history
The text no longer accepts min and max: `text(min, max)` -> `text()`

Varchar's limit becomes required: `varchar(limit?: number)` -> `varchar(limit: number)`
  • Loading branch information
romeerez committed Jun 20, 2024
1 parent 7c56a4f commit 37cf7b7
Show file tree
Hide file tree
Showing 45 changed files with 212 additions and 312 deletions.
17 changes: 17 additions & 0 deletions .changeset/shiny-wolves-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'orchid-orm-valibot': minor
'orchid-orm-schema-to-zod': minor
'orchid-orm-test-factory': minor
'create-orchid-orm': minor
'test-utils': minor
'rake-db': minor
'pqb': minor
'orchid-core': minor
'orchid-orm': minor
---

Change `text`, `varchar` types, remove `char` (#277)

The text no longer accepts min and max: `text(min, max)` -> `text()`

Varchar's limit becomes required: `varchar(limit?: number)` -> `varchar(limit: number)`
16 changes: 16 additions & 0 deletions BREAKING_CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Breaking changes

## orchid-orm 1.30.0

The `text` column type no longer accepts `min` and `max` params.

If you have overridden it in the `BaseTable` config like the following, simply remove it:

```ts
text: (min = 0, max = Infinity) => t.text(min, max),
```

Replace all occurrences of `text(min, max)` in your code with `text().min(min).max(max)`.

`varchar`'s limit parameter becomes required, replace unlimited varchars with `text`.

The `char` type is removed because it's [discouraged](https://wiki.postgresql.org/wiki/Don't_Do_This#Don.27t_use_char.28n.29) by Postgres.

## orchid-orm 1.29.0

`json*` methods rework: now all json methods such as `jsonSet` can be used in all contexts on a single JSON value,
Expand Down
22 changes: 10 additions & 12 deletions docs/src/guide/columns-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Different types of columns support different operations in `where` conditions:
export class SomeTable extends BaseTable {
readonly table = 'someTable';
columns = this.setColumns((t) => ({
name: t.text(3, 100),
name: t.string(),
age: t.integer(),
}));
}
Expand Down Expand Up @@ -202,30 +202,28 @@ ORM doesn't validate inputs by itself,
use `Table.inputSchema()` (see [Validation methods](/guide/columns-validation-methods)) in your request handlers,
and then it's guaranteed that user won't be able to submit empty or a million chars long username and other text data.

Type of `text` method forces you to provide the `min` and `max` each time when calling it. To simplify this, define common defaults for all text columns:
It's important to validate min and max lengths of texts submitted by users,
because if it's not validated, users are free to submit empty or endless texts.

You may want to force the `text` type to force min and max parameters for validation:

```ts
export const BaseTable = createBaseTable({
columnTypes: (t) => ({
...t,
text: (min = 3, max = 100) => t.text(min, max),
text: (min: number, max: number) => t.text().min(min).max(max),
}),
});
```

With such config, all text columns will be validated to have at least 3 and at most 100 characters.
With such config, all text columns will require explicit min and max parameters:

```ts
export class SomeTable extends BaseTable {
readonly table = 'someTable';
export class PostTable extends BaseTable {
readonly table = 'post';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
// name will be validated to have at least 3 and at most 100 chars
name: t.text(),
// override min
password: t.text().min(8),
// override max
bio: t.text().max(1000),
content: t.text(3, 10000),
}));
}
```
27 changes: 11 additions & 16 deletions docs/src/guide/columns-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,18 @@ db.someTable.where({

## text

Use `t.text(min, max)` type as a go-to for strings, other types are for special cases.

`min` and `max` number parameters defines a validation of string length, they are required to ensure that the app won't accept empty or enormous values from user.

These parameters are not required on the `text` method in migrations, because they don't affect on a database column type.
- `t.text()` is for an unlimited database `text` type.
- `t.varchar(limit: number)` is for text with a limit on a database level.
- `t.string(limit = 255)` is the same as `varchar` with 255 default limit.

```ts
// character varying(n), varchar(n) variable-length with limit
t.varchar(limit?: number) // -> string

// character(n), char(n) fixed-length, blank padded
t.chat(limit?: number) // -> string
// text with unlimited length
t.text() // -> string

// text variable unlimited length
t.text(min: number, max: number) // -> string
// variable-length text with limit
t.varchar(limit: number) // -> string

// `varchar` column with optional limit defaulting to 255.
// `varchar` type with optional limit defaulting to 255.
t.string(limit?: number = 255) // -> string
```

Expand All @@ -117,6 +112,8 @@ db.someTable.where({
});
```

The `char` database type isn't added because it is [discouraged](https://wiki.postgresql.org/wiki/Don't_Do_This#Don.27t_use_char.28n.29) by Postgres.

## citext

[citext](https://www.postgresql.org/docs/current/citext.html) is a database type that behaves almost exactly like `text`,
Expand All @@ -142,11 +139,9 @@ npm run db migrate

And now `citext` is available and can be used just as a `text` type.

It requires `min` and `max`, but can be [overridden](/guide/columns-overview#override-column-types) in the same way as the `text`.

```ts
// text variable unlimited length
t.citext(min: number, max: number) // -> string
t.citext() // -> string
```

## tsvector
Expand Down
14 changes: 7 additions & 7 deletions docs/src/guide/common-column-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export class Table extends BaseTable {
columns = this.setColumns((t) => ({
// encode boolean, number, or string to text before saving
column: t
.text(3, 100)
.string()
// when having validation library, the first argument is a validation schema
.encode(
z.boolean().or(z.number()).or(z.string()),
Expand Down Expand Up @@ -201,14 +201,14 @@ export class Table extends BaseTable {
readonly table = 'table';
columns = this.setColumns((t) => ({
columnZod: t
.text(3, 100)
.string()
// when having validation library, the first argument is a schema
.parse(z.number().int(), (input) => parseInt(input))
// no schema argument otherwise
.parse((input) => parseInt(input)),

columnValibot: t
.text(3, 100)
.string()
.parse(number([integer()]), (input) => parseInt(input))
.parse((input) => parseInt(input)),
}));
Expand All @@ -228,7 +228,7 @@ export class Table extends BaseTable {
// return a default image URL if it is null
// this allows to change the defaultImageURL without modifying a database
imageURL: t
.text(5, 300)
.varchar(1000)
.nullable()
.parse((url) => url ?? defaultImageURL),
}));
Expand All @@ -247,7 +247,7 @@ and `.parse` which returns the correct type.
```ts
// column has the same type as t.integer()
const column = t
.text(1, 100)
.string()
.encode((input: number) => input)
.parse((text) => parseInt(text))
// schema argument is required if you included a validation library
Expand All @@ -269,7 +269,7 @@ If you don't specify `schemaConfig` option for a [validation library](/guide/col
export class Table extends BaseTable {
readonly table = 'table';
columns = this.setColumns((t) => ({
size: t.text().asType((t) => t<'small' | 'medium' | 'large'>()),
size: t.string().asType((t) => t<'small' | 'medium' | 'large'>()),
}));
}

Expand Down Expand Up @@ -373,7 +373,7 @@ When mutating a query in this callback, the changes will be applied to all futur
export class SomeTable extends BaseTable {
readonly table = 'someTable';
columns = this.setColumns((t) => ({
name: t.text(3, 100).modifyQuery((table, column) => {
name: t.string().modifyQuery((table, column) => {
// table argument is the query interface of SomeTable
// column object contains data with column name and other properties
}),
Expand Down
4 changes: 2 additions & 2 deletions docs/src/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ export class UserTable extends BaseTable {
readonly table = 'user';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 30), // 3 characters minimum, 30 maximum
password: t.text(8, 200),
name: t.string(), // `string` is varchar with 255 default limit
password: t.varchar(50), // 50 chars max
// adds createdAt and updatedAt with defaults:
...t.timestamps(),
}));
Expand Down
8 changes: 4 additions & 4 deletions docs/src/guide/orm-and-query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ export class UserTable extends BaseTable {
readonly table = 'user';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
password: t.text(8, 200),
name: t.string(),
password: t.string(),
...t.timestamps(),
}));
}
Expand Down Expand Up @@ -457,8 +457,8 @@ After `db` is defined, construct queryable tables in such way:
```ts
export const User = db('user', (t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
password: t.text(8, 200),
name: t.string(),
password: t.string(),
age: t.integer().nullable(),
...t.timestamps(),
}));
Expand Down
26 changes: 13 additions & 13 deletions docs/src/guide/relations.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class AuthorTable extends BaseTable {
readonly table = 'author';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
name: t.string(),
}));
}

Expand All @@ -73,7 +73,7 @@ export class BookTable extends BaseTable {
readonly table = 'book';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
title: t.text(5, 100),
title: t.string(),
// book has a column pointing to the author table
authorId: t.integer(),
}));
Expand Down Expand Up @@ -108,7 +108,7 @@ export class SupplierTable extends BaseTable {
readonly table = 'supplier';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
brand: t.text(2, 30),
brand: t.string(),
// here are no reference columns for an Account
}));

Expand All @@ -130,7 +130,7 @@ export class AccountTable extends BaseTable {
readonly table = 'account';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
name: t.string(),
// Account has a column pointing to Supplier:
supplierId: t.integer(),
}));
Expand All @@ -155,7 +155,7 @@ export class SupplierTable extends BaseTable {
readonly table = 'supplier';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
brand: t.text(2, 30),
brand: t.string(),
}));

relations = {
Expand All @@ -180,7 +180,7 @@ export class AccountTable extends BaseTable {
readonly table = 'account';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
name: t.string(),
// Account has a column pointing to Supplier:
supplierId: t.integer(),
}));
Expand All @@ -199,7 +199,7 @@ export class AccountHistoryTable extends BaseTable {
readonly table = 'accountHistory';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
data: t.text(0, 1000),
data: t.text(),
// column pointing to the Account
accountId: t.integer(),
}));
Expand Down Expand Up @@ -231,7 +231,7 @@ export class AuthorTable extends BaseTable {
readonly table = 'author';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
name: t.string(),
}));

relations = {
Expand All @@ -249,7 +249,7 @@ export class BookTable extends BaseTable {
readonly table = 'book';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
title: t.text(3, 100),
title: t.string(),
// book has a column pointing to the author table
authorId: t.integer(),
}));
Expand All @@ -274,7 +274,7 @@ export class PhysicianTable extends BaseTable {
readonly table = 'physician';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
name: t.string(),
}));

relations = {
Expand Down Expand Up @@ -324,7 +324,7 @@ export class PatientTable extends BaseTable {
readonly table = 'patient';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
name: t.string(),
}));

relations = {
Expand Down Expand Up @@ -363,7 +363,7 @@ export class PostTable extends BaseTable {
readonly table = 'post';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
title: t.text(5, 100),
title: t.string(),
}));

relations = {
Expand All @@ -389,7 +389,7 @@ export class TagTable extends BaseTable {
readonly table = 'tag';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
name: t.text(3, 100),
name: t.string(),
}));

relations = {
Expand Down
6 changes: 3 additions & 3 deletions docs/src/guide/test-factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,9 @@ class SomeTable extends BaseTable {
readonly table = 'table';
columns = this.setColumns((t) => ({
id: t.identity().primaryKey(),
text: t.text(5, 1000).unique(),
email: t.text(3, 100).email().unique(),
url: t.text(10, 200).url().unique(),
text: t.text(),
email: t.string().email().unique(),
url: t.varchar(1000).url().unique(),
number: t.integer().unique(),
greaterThan10: t.integer().gt(10).unique(),
greaterThanOrEqualTo10: t.integer().gte(10).unique(),
Expand Down
2 changes: 1 addition & 1 deletion docs/src/guide/where.md
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ db.table.where({

### text operators

For `text`, `char`, `varchar`, and `json` columns.
For `text`, `varchar`, `string`, and `json` columns.

`json` is stored as text, so it has text operators. Use the `jsonb` type for JSON operators.

Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/columns/columnSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,10 @@ export interface ColumnSchemaConfig<T extends ColumnTypeBase = ColumnTypeBase>
doublePrecision(): T;
bigSerial(): T;
money(): T;
varchar(limit?: number): T;
char(limit?: number): T;
text(min: number, max: number): T;
varchar(limit: number): T;
text(): T;
string(limit?: number): T;
citext(min: number, max: number): T;
citext(): T;

date(): T;
timestampNoTZ(precision?: number): T;
Expand Down
Loading

0 comments on commit 37cf7b7

Please sign in to comment.