pqb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3630 -0
- package/dist/index.esm.js +4587 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +4691 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/rollup.config.js +35 -0
- package/src/adapter.test.ts +10 -0
- package/src/adapter.ts +171 -0
- package/src/columnSchema/array.ts +21 -0
- package/src/columnSchema/boolean.ts +10 -0
- package/src/columnSchema/columnType.test.ts +129 -0
- package/src/columnSchema/columnType.ts +77 -0
- package/src/columnSchema/columnTypes.ts +145 -0
- package/src/columnSchema/columnsSchema.test.ts +32 -0
- package/src/columnSchema/columnsSchema.ts +100 -0
- package/src/columnSchema/commonMethods.ts +130 -0
- package/src/columnSchema/dateTime.ts +104 -0
- package/src/columnSchema/enum.ts +13 -0
- package/src/columnSchema/index.ts +11 -0
- package/src/columnSchema/json/array.ts +55 -0
- package/src/columnSchema/json/discriminatedUnion.ts +91 -0
- package/src/columnSchema/json/enum.ts +29 -0
- package/src/columnSchema/json/instanceOf.ts +16 -0
- package/src/columnSchema/json/intersection.ts +23 -0
- package/src/columnSchema/json/lazy.ts +22 -0
- package/src/columnSchema/json/literal.ts +12 -0
- package/src/columnSchema/json/map.ts +29 -0
- package/src/columnSchema/json/nativeEnum.ts +30 -0
- package/src/columnSchema/json/nullable.ts +33 -0
- package/src/columnSchema/json/nullish.ts +30 -0
- package/src/columnSchema/json/object.ts +206 -0
- package/src/columnSchema/json/optional.ts +28 -0
- package/src/columnSchema/json/record.ts +40 -0
- package/src/columnSchema/json/scalarTypes.ts +117 -0
- package/src/columnSchema/json/set.ts +34 -0
- package/src/columnSchema/json/tuple.ts +40 -0
- package/src/columnSchema/json/typeBase.ts +202 -0
- package/src/columnSchema/json/union.ts +16 -0
- package/src/columnSchema/json.ts +64 -0
- package/src/columnSchema/number.ts +122 -0
- package/src/columnSchema/string.ts +222 -0
- package/src/columnSchema/utils.ts +27 -0
- package/src/common.ts +86 -0
- package/src/db.test.ts +67 -0
- package/src/db.ts +212 -0
- package/src/errors.ts +7 -0
- package/src/index.ts +18 -0
- package/src/operators.test.ts +608 -0
- package/src/operators.ts +177 -0
- package/src/query.ts +292 -0
- package/src/queryDataUtils.ts +50 -0
- package/src/queryMethods/aggregate.test.ts +583 -0
- package/src/queryMethods/aggregate.ts +878 -0
- package/src/queryMethods/callbacks.test.ts +69 -0
- package/src/queryMethods/callbacks.ts +55 -0
- package/src/queryMethods/clear.test.ts +64 -0
- package/src/queryMethods/clear.ts +58 -0
- package/src/queryMethods/columnInfo.test.ts +45 -0
- package/src/queryMethods/columnInfo.ts +67 -0
- package/src/queryMethods/delete.test.ts +135 -0
- package/src/queryMethods/delete.ts +50 -0
- package/src/queryMethods/for.test.ts +57 -0
- package/src/queryMethods/for.ts +99 -0
- package/src/queryMethods/from.test.ts +66 -0
- package/src/queryMethods/from.ts +58 -0
- package/src/queryMethods/get.test.ts +66 -0
- package/src/queryMethods/get.ts +88 -0
- package/src/queryMethods/having.test.ts +247 -0
- package/src/queryMethods/having.ts +99 -0
- package/src/queryMethods/insert.test.ts +555 -0
- package/src/queryMethods/insert.ts +453 -0
- package/src/queryMethods/join.test.ts +150 -0
- package/src/queryMethods/join.ts +508 -0
- package/src/queryMethods/json.test.ts +398 -0
- package/src/queryMethods/json.ts +259 -0
- package/src/queryMethods/log.test.ts +172 -0
- package/src/queryMethods/log.ts +123 -0
- package/src/queryMethods/queryMethods.test.ts +629 -0
- package/src/queryMethods/queryMethods.ts +428 -0
- package/src/queryMethods/select.test.ts +479 -0
- package/src/queryMethods/select.ts +249 -0
- package/src/queryMethods/then.ts +236 -0
- package/src/queryMethods/transaction.test.ts +66 -0
- package/src/queryMethods/transaction.ts +66 -0
- package/src/queryMethods/union.test.ts +59 -0
- package/src/queryMethods/union.ts +89 -0
- package/src/queryMethods/update.test.ts +417 -0
- package/src/queryMethods/update.ts +350 -0
- package/src/queryMethods/upsert.test.ts +56 -0
- package/src/queryMethods/upsert.ts +43 -0
- package/src/queryMethods/where.test.ts +1594 -0
- package/src/queryMethods/where.ts +450 -0
- package/src/queryMethods/window.test.ts +66 -0
- package/src/queryMethods/window.ts +108 -0
- package/src/queryMethods/with.test.ts +191 -0
- package/src/queryMethods/with.ts +92 -0
- package/src/quote.ts +36 -0
- package/src/relations.ts +194 -0
- package/src/sql/aggregate.ts +80 -0
- package/src/sql/columnInfo.ts +22 -0
- package/src/sql/common.ts +42 -0
- package/src/sql/delete.ts +41 -0
- package/src/sql/distinct.ts +19 -0
- package/src/sql/fromAndAs.ts +51 -0
- package/src/sql/having.ts +140 -0
- package/src/sql/index.ts +2 -0
- package/src/sql/insert.ts +102 -0
- package/src/sql/join.ts +242 -0
- package/src/sql/orderBy.ts +41 -0
- package/src/sql/select.ts +153 -0
- package/src/sql/toSql.ts +153 -0
- package/src/sql/truncate.ts +13 -0
- package/src/sql/types.ts +355 -0
- package/src/sql/update.ts +62 -0
- package/src/sql/where.ts +314 -0
- package/src/sql/window.ts +38 -0
- package/src/sql/with.ts +32 -0
- package/src/test-utils.ts +172 -0
- package/src/utils.ts +140 -0
- package/tsconfig.build.json +6 -0
- package/tsconfig.json +8 -0
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pqb",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Postgres query builder",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/romeerez/porm"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"module": "dist/index.esm.js",
|
|
11
|
+
"typings": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"require": "./dist/index.js",
|
|
15
|
+
"import": "./dist/index.esm.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"jest": {
|
|
20
|
+
"setupFiles": [
|
|
21
|
+
"dotenv/config"
|
|
22
|
+
],
|
|
23
|
+
"globalSetup": "../../jest-global-setup.ts",
|
|
24
|
+
"setupFilesAfterEnv": [
|
|
25
|
+
"../../jest-setup.ts"
|
|
26
|
+
],
|
|
27
|
+
"transform": {
|
|
28
|
+
"^.+\\.ts$": "@swc/jest"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"postgres",
|
|
33
|
+
"query-builder"
|
|
34
|
+
],
|
|
35
|
+
"author": "Roman Kushyn",
|
|
36
|
+
"license": "ISC",
|
|
37
|
+
"dependencies": {},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"rollup": "^2.79.0",
|
|
40
|
+
"rollup-plugin-dts": "^4.2.2",
|
|
41
|
+
"rollup-plugin-esbuild": "^4.10.1",
|
|
42
|
+
"@swc/core": "^1.2.210",
|
|
43
|
+
"@swc/jest": "^0.2.21",
|
|
44
|
+
"@types/jest": "^28.1.2",
|
|
45
|
+
"@types/node": "^18.0.1",
|
|
46
|
+
"@types/pg": "^8.6.5",
|
|
47
|
+
"dotenv": "^16.0.1",
|
|
48
|
+
"jest": "^28.1.2",
|
|
49
|
+
"pg": "^8.7.3",
|
|
50
|
+
"pg-transactional-tests": "^1.0.3",
|
|
51
|
+
"rimraf": "^3.0.2",
|
|
52
|
+
"tslib": "^2.4.0",
|
|
53
|
+
"typescript": "^4.7.4"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"test": "jest",
|
|
57
|
+
"build": "rimraf ./dist/ && rollup -c --rollup.config"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import dts from 'rollup-plugin-dts';
|
|
2
|
+
import esbuild from 'rollup-plugin-esbuild';
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
{
|
|
6
|
+
input: 'src/index.ts',
|
|
7
|
+
plugins: [esbuild()],
|
|
8
|
+
output: [
|
|
9
|
+
{
|
|
10
|
+
file: 'dist/index.js',
|
|
11
|
+
format: 'cjs',
|
|
12
|
+
sourcemap: true,
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
input: 'src/index.ts',
|
|
18
|
+
plugins: [esbuild()],
|
|
19
|
+
output: [
|
|
20
|
+
{
|
|
21
|
+
file: 'dist/index.esm.js',
|
|
22
|
+
format: 'es',
|
|
23
|
+
sourcemap: true,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
input: 'src/index.ts',
|
|
29
|
+
plugins: [dts()],
|
|
30
|
+
output: {
|
|
31
|
+
file: `dist/index.d.ts`,
|
|
32
|
+
format: 'es',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { adapter } from './test-utils';
|
|
2
|
+
|
|
3
|
+
describe('adapter', () => {
|
|
4
|
+
it('should run query and close connection by calling .destroy()', async () => {
|
|
5
|
+
const result = await adapter.query('SELECT 1 as num');
|
|
6
|
+
expect(result.rows).toEqual([{ num: 1 }]);
|
|
7
|
+
|
|
8
|
+
await adapter.destroy();
|
|
9
|
+
});
|
|
10
|
+
});
|
package/src/adapter.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { Pool, PoolClient, PoolConfig, types } from 'pg';
|
|
2
|
+
|
|
3
|
+
export interface QueryResultRow {
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
[column: string]: any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type TypeParsers = Record<number, (input: string) => unknown>;
|
|
9
|
+
|
|
10
|
+
type Query = string | { text: string; values?: unknown[] };
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
export type QueryResult<T extends QueryResultRow = any> = {
|
|
14
|
+
rowCount: number;
|
|
15
|
+
rows: T[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
export type QueryArraysResult<R extends any[] = any[]> = {
|
|
20
|
+
rowCount: number;
|
|
21
|
+
rows: R[];
|
|
22
|
+
fields: { name: string }[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const defaultTypeParsers: TypeParsers = {};
|
|
26
|
+
|
|
27
|
+
for (const key in types.builtins) {
|
|
28
|
+
const id = types.builtins[key as keyof typeof types.builtins];
|
|
29
|
+
defaultTypeParsers[id] = types.getTypeParser(id);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
[
|
|
33
|
+
types.builtins.DATE,
|
|
34
|
+
types.builtins.TIMESTAMP,
|
|
35
|
+
types.builtins.TIMESTAMPTZ,
|
|
36
|
+
types.builtins.TIME,
|
|
37
|
+
].forEach((id) => {
|
|
38
|
+
delete defaultTypeParsers[id];
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const returnArg = (arg: unknown) => arg;
|
|
42
|
+
|
|
43
|
+
export type AdapterOptions = Omit<PoolConfig, 'types'> & {
|
|
44
|
+
types?: TypeParsers;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export class Adapter {
|
|
48
|
+
types: TypeParsers;
|
|
49
|
+
pool: Pool;
|
|
50
|
+
|
|
51
|
+
constructor({ types = defaultTypeParsers, ...config }: AdapterOptions) {
|
|
52
|
+
this.types = types;
|
|
53
|
+
this.pool = new Pool(config);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
async query<T extends QueryResultRow = any>(
|
|
58
|
+
query: Query,
|
|
59
|
+
types: TypeParsers = this.types,
|
|
60
|
+
): Promise<QueryResult<T>> {
|
|
61
|
+
const client = await this.pool.connect();
|
|
62
|
+
try {
|
|
63
|
+
return await performQuery<T>(client, query, types);
|
|
64
|
+
} finally {
|
|
65
|
+
client.release();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
async arrays<R extends any[] = any[]>(
|
|
71
|
+
query: Query,
|
|
72
|
+
types: TypeParsers = this.types,
|
|
73
|
+
): Promise<QueryArraysResult<R>> {
|
|
74
|
+
const client = await this.pool.connect();
|
|
75
|
+
try {
|
|
76
|
+
return await performQueryArrays<R>(client, query, types);
|
|
77
|
+
} finally {
|
|
78
|
+
client.release();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async transaction<Result>(
|
|
83
|
+
cb: (adapter: Adapter) => Promise<Result>,
|
|
84
|
+
): Promise<Result> {
|
|
85
|
+
const client = await this.pool.connect();
|
|
86
|
+
try {
|
|
87
|
+
await performQuery(client, { text: 'BEGIN' }, this.types);
|
|
88
|
+
const result = await cb(
|
|
89
|
+
new TransactionAdapter(this.pool, client, this.types),
|
|
90
|
+
);
|
|
91
|
+
await performQuery(client, { text: 'COMMIT' }, this.types);
|
|
92
|
+
return result;
|
|
93
|
+
} catch (err) {
|
|
94
|
+
await performQuery(client, { text: 'ROLLBACK' }, this.types);
|
|
95
|
+
throw err;
|
|
96
|
+
} finally {
|
|
97
|
+
client.release();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
destroy(): Promise<void> {
|
|
101
|
+
return this.pool.end();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const performQuery = <T extends QueryResultRow>(
|
|
106
|
+
client: PoolClient,
|
|
107
|
+
query: Query,
|
|
108
|
+
types: TypeParsers,
|
|
109
|
+
) => {
|
|
110
|
+
return client.query<T>({
|
|
111
|
+
text: typeof query === 'string' ? query : query.text,
|
|
112
|
+
values: typeof query === 'string' ? undefined : query.values,
|
|
113
|
+
types: types && {
|
|
114
|
+
getTypeParser(id: number) {
|
|
115
|
+
return types[id] || returnArg;
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
const performQueryArrays = <T extends any[] = any[]>(
|
|
123
|
+
client: PoolClient,
|
|
124
|
+
query: Query,
|
|
125
|
+
types: TypeParsers,
|
|
126
|
+
) => {
|
|
127
|
+
return client.query<T>({
|
|
128
|
+
text: typeof query === 'string' ? query : query.text,
|
|
129
|
+
values: typeof query === 'string' ? undefined : query.values,
|
|
130
|
+
rowMode: 'array',
|
|
131
|
+
types: types && {
|
|
132
|
+
getTypeParser(id: number) {
|
|
133
|
+
return types[id] || returnArg;
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export class TransactionAdapter implements Adapter {
|
|
140
|
+
constructor(
|
|
141
|
+
public pool: Pool,
|
|
142
|
+
public client: PoolClient,
|
|
143
|
+
public types: TypeParsers,
|
|
144
|
+
) {}
|
|
145
|
+
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
147
|
+
async query<T extends QueryResultRow = any>(
|
|
148
|
+
query: Query,
|
|
149
|
+
types: TypeParsers = this.types,
|
|
150
|
+
): Promise<QueryResult<T>> {
|
|
151
|
+
return await performQuery<T>(this.client, query, types);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
+
async arrays<R extends any[] = any[]>(
|
|
156
|
+
query: Query,
|
|
157
|
+
types: TypeParsers = this.types,
|
|
158
|
+
): Promise<QueryArraysResult<R>> {
|
|
159
|
+
return await performQueryArrays<R>(this.client, query, types);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async transaction<Result>(
|
|
163
|
+
cb: (adapter: Adapter) => Promise<Result>,
|
|
164
|
+
): Promise<Result> {
|
|
165
|
+
return await cb(this);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
destroy() {
|
|
169
|
+
return this.pool.end();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ColumnType } from './columnType';
|
|
2
|
+
import { Operators } from '../operators';
|
|
3
|
+
|
|
4
|
+
export class ArrayColumn<Item extends ColumnType> extends ColumnType<
|
|
5
|
+
Item['type'][],
|
|
6
|
+
typeof Operators.array
|
|
7
|
+
> {
|
|
8
|
+
dataType = 'array' as const;
|
|
9
|
+
operators = Operators.array;
|
|
10
|
+
data: { item: Item };
|
|
11
|
+
|
|
12
|
+
constructor(item: Item) {
|
|
13
|
+
super();
|
|
14
|
+
|
|
15
|
+
this.data = { item };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
toSQL() {
|
|
19
|
+
return `${this.data.item.toSQL()}[]`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { ColumnType } from './columnType';
|
|
2
|
+
import { Operators } from '../operators';
|
|
3
|
+
import {
|
|
4
|
+
adapter,
|
|
5
|
+
AssertEqual,
|
|
6
|
+
db,
|
|
7
|
+
expectSql,
|
|
8
|
+
User,
|
|
9
|
+
userData,
|
|
10
|
+
useTestDatabase,
|
|
11
|
+
} from '../test-utils';
|
|
12
|
+
import { createDb } from '../db';
|
|
13
|
+
|
|
14
|
+
describe('column base', () => {
|
|
15
|
+
useTestDatabase();
|
|
16
|
+
|
|
17
|
+
class Column extends ColumnType {
|
|
18
|
+
dataType = 'test';
|
|
19
|
+
operators = Operators.any;
|
|
20
|
+
}
|
|
21
|
+
const column = new Column();
|
|
22
|
+
|
|
23
|
+
describe('.primaryKey', () => {
|
|
24
|
+
it('should mark column as a primary key', () => {
|
|
25
|
+
expect(column.isPrimaryKey).toBe(false);
|
|
26
|
+
expect(column.primaryKey().isPrimaryKey).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('.hidden', () => {
|
|
31
|
+
it('should mark column as hidden', () => {
|
|
32
|
+
expect(column.isHidden).toBe(false);
|
|
33
|
+
expect(column.hidden().isHidden).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('model with hidden column should omit from select it by default', () => {
|
|
37
|
+
const User = db('user', (t) => ({
|
|
38
|
+
id: t.serial().primaryKey(),
|
|
39
|
+
name: t.text(),
|
|
40
|
+
password: t.text().hidden(),
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
const q = User.all();
|
|
44
|
+
expectSql(
|
|
45
|
+
q.toSql(),
|
|
46
|
+
`
|
|
47
|
+
SELECT
|
|
48
|
+
"user"."id",
|
|
49
|
+
"user"."name"
|
|
50
|
+
FROM "user"
|
|
51
|
+
`,
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('model with hidden column still allows to select it', () => {
|
|
56
|
+
const User = db('user', (t) => ({
|
|
57
|
+
id: t.serial().primaryKey(),
|
|
58
|
+
name: t.text(),
|
|
59
|
+
password: t.text().hidden(),
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
const q = User.select('id', 'name', 'password');
|
|
63
|
+
expectSql(
|
|
64
|
+
q.toSql(),
|
|
65
|
+
`
|
|
66
|
+
SELECT
|
|
67
|
+
"user"."id",
|
|
68
|
+
"user"."name",
|
|
69
|
+
"user"."password"
|
|
70
|
+
FROM "user"
|
|
71
|
+
`,
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('.nullable', () => {
|
|
77
|
+
it('should mark column as nullable', () => {
|
|
78
|
+
expect(column.isNullable).toBe(false);
|
|
79
|
+
expect(column.nullable().isNullable).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('.encodeFn', () => {
|
|
84
|
+
it('should set a function to encode value for this column', () => {
|
|
85
|
+
expect(column.encodeFn).toBe(undefined);
|
|
86
|
+
const fn = (input: number) => input.toString();
|
|
87
|
+
const withEncode = column.encode(fn);
|
|
88
|
+
expect(withEncode.encodeFn).toBe(fn);
|
|
89
|
+
const eq: AssertEqual<typeof withEncode.inputType, number> = true;
|
|
90
|
+
expect(eq).toBeTruthy();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('.parseFn', () => {
|
|
95
|
+
it('should set a function to encode value for this column', () => {
|
|
96
|
+
expect(column.parseFn).toBe(undefined);
|
|
97
|
+
const fn = () => 123;
|
|
98
|
+
const withEncode = column.parse(fn);
|
|
99
|
+
expect(withEncode.parseFn).toBe(fn);
|
|
100
|
+
const eq: AssertEqual<typeof withEncode.type, number> = true;
|
|
101
|
+
expect(eq).toBeTruthy();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('parsing columns', () => {
|
|
105
|
+
beforeEach(async () => {
|
|
106
|
+
await User.insert(userData);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should return column data as returned from db if not set', async () => {
|
|
110
|
+
const db = createDb({ adapter });
|
|
111
|
+
|
|
112
|
+
const UserWithPlainTimestamp = db('user', (t) => ({
|
|
113
|
+
createdAt: t.timestamp(),
|
|
114
|
+
}));
|
|
115
|
+
|
|
116
|
+
expect(typeof (await UserWithPlainTimestamp.take()).createdAt).toBe(
|
|
117
|
+
'string',
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should parse all columns', async () => {
|
|
122
|
+
expect((await User.all())[0].createdAt instanceof Date).toBe(true);
|
|
123
|
+
expect((await User.take()).createdAt instanceof Date).toBe(true);
|
|
124
|
+
const idx = Object.keys(User.shape).indexOf('createdAt');
|
|
125
|
+
expect((await User.rows())[0][idx] instanceof Date).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Operator, Operators } from '../operators';
|
|
2
|
+
import { EmptyObject } from './utils';
|
|
3
|
+
|
|
4
|
+
export type ColumnOutput<T extends ColumnType> = T['type'];
|
|
5
|
+
|
|
6
|
+
export type ColumnInput<T extends ColumnType> = T['inputType'];
|
|
7
|
+
|
|
8
|
+
export type NullableColumn<T extends ColumnType> = Omit<
|
|
9
|
+
T,
|
|
10
|
+
'type' | 'operators'
|
|
11
|
+
> & {
|
|
12
|
+
type: T['type'] | null;
|
|
13
|
+
isNullable: true;
|
|
14
|
+
operators: Omit<T['operators'], 'equals' | 'not'> & {
|
|
15
|
+
equals: Operator<T['type'] | null>;
|
|
16
|
+
not: Operator<T['type'] | null>;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
export type AnyColumnType = ColumnType<any, Record<string, Operator<any>>>;
|
|
22
|
+
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
export type AnyColumnTypeCreator = (...args: any[]) => AnyColumnType;
|
|
25
|
+
|
|
26
|
+
export abstract class ColumnType<
|
|
27
|
+
Type = unknown,
|
|
28
|
+
Ops extends Operators = Operators,
|
|
29
|
+
InputType = Type,
|
|
30
|
+
> {
|
|
31
|
+
abstract dataType: string;
|
|
32
|
+
abstract operators: Ops;
|
|
33
|
+
|
|
34
|
+
type!: Type;
|
|
35
|
+
inputType!: InputType;
|
|
36
|
+
data = {} as EmptyObject;
|
|
37
|
+
isPrimaryKey = false;
|
|
38
|
+
isHidden = false;
|
|
39
|
+
isNullable = false;
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
encodeFn?: (input: any) => unknown;
|
|
42
|
+
parseFn?: (input: unknown) => unknown;
|
|
43
|
+
|
|
44
|
+
primaryKey<T extends ColumnType>(this: T): T & { isPrimaryKey: true } {
|
|
45
|
+
return Object.assign(this, { isPrimaryKey: true as const });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
hidden<T extends ColumnType>(this: T): T & { isHidden: true } {
|
|
49
|
+
return Object.assign(this, { isHidden: true as const });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
nullable<T extends ColumnType>(this: T): NullableColumn<T> {
|
|
53
|
+
this.isNullable = true;
|
|
54
|
+
return this as unknown as NullableColumn<T>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
encode<T extends ColumnType, Input>(
|
|
58
|
+
this: T,
|
|
59
|
+
fn: (input: Input) => unknown,
|
|
60
|
+
): Omit<T, 'inputType'> & { inputType: Input } {
|
|
61
|
+
const self = this as unknown as Omit<T, 'inputType'> & { inputType: Input };
|
|
62
|
+
self.encodeFn = fn;
|
|
63
|
+
return self;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
parse<T extends ColumnType, Output>(
|
|
67
|
+
this: T,
|
|
68
|
+
fn: (input: T['type']) => Output,
|
|
69
|
+
): Omit<T, 'type'> & { type: Output } {
|
|
70
|
+
this.parseFn = fn;
|
|
71
|
+
return this as unknown as Omit<T, 'type'> & { type: Output };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
toSQL() {
|
|
75
|
+
return this.dataType;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BigIntColumn,
|
|
3
|
+
BigSerialColumn,
|
|
4
|
+
DecimalBigIntColumn,
|
|
5
|
+
DecimalColumn,
|
|
6
|
+
DoublePrecisionColumn,
|
|
7
|
+
IntegerColumn,
|
|
8
|
+
RealColumn,
|
|
9
|
+
SerialColumn,
|
|
10
|
+
SmallIntColumn,
|
|
11
|
+
SmallSerialColumn,
|
|
12
|
+
} from './number';
|
|
13
|
+
import {
|
|
14
|
+
BitColumn,
|
|
15
|
+
BitVaryingColumn,
|
|
16
|
+
BoxColumn,
|
|
17
|
+
ByteaColumn,
|
|
18
|
+
CharColumn,
|
|
19
|
+
CidrColumn,
|
|
20
|
+
CircleColumn,
|
|
21
|
+
InetColumn,
|
|
22
|
+
LineColumn,
|
|
23
|
+
LsegColumn,
|
|
24
|
+
MacAddrColumn,
|
|
25
|
+
MacAddr8Column,
|
|
26
|
+
MoneyColumn,
|
|
27
|
+
PathColumn,
|
|
28
|
+
PointColumn,
|
|
29
|
+
PolygonColumn,
|
|
30
|
+
TextColumn,
|
|
31
|
+
TsQueryColumn,
|
|
32
|
+
TsVectorColumn,
|
|
33
|
+
UUIDColumn,
|
|
34
|
+
VarCharColumn,
|
|
35
|
+
XMLColumn,
|
|
36
|
+
} from './string';
|
|
37
|
+
import {
|
|
38
|
+
DateColumn,
|
|
39
|
+
IntervalColumn,
|
|
40
|
+
TimeColumn,
|
|
41
|
+
TimestampColumn,
|
|
42
|
+
TimestampWithTimeZoneColumn,
|
|
43
|
+
TimeWithTimeZoneColumn,
|
|
44
|
+
} from './dateTime';
|
|
45
|
+
import { BooleanColumn } from './boolean';
|
|
46
|
+
import { EnumColumn } from './enum';
|
|
47
|
+
import { JSONColumn, JSONTextColumn, JSONTypes } from './json';
|
|
48
|
+
import { JSONTypeAny } from './json/typeBase';
|
|
49
|
+
import { ArrayColumn } from './array';
|
|
50
|
+
import { ColumnType } from './columnType';
|
|
51
|
+
|
|
52
|
+
export type ColumnTypes = typeof columnTypes;
|
|
53
|
+
|
|
54
|
+
export const columnTypes = {
|
|
55
|
+
smallint: () => new SmallIntColumn(),
|
|
56
|
+
integer: () => new IntegerColumn(),
|
|
57
|
+
bigint: () => new BigIntColumn(),
|
|
58
|
+
numeric: <
|
|
59
|
+
Precision extends number | undefined = undefined,
|
|
60
|
+
Scale extends number | undefined = undefined,
|
|
61
|
+
>(
|
|
62
|
+
precision?: Precision,
|
|
63
|
+
scale?: Scale,
|
|
64
|
+
) => new DecimalColumn(precision, scale),
|
|
65
|
+
numericBigInt: <
|
|
66
|
+
Precision extends number | undefined = undefined,
|
|
67
|
+
Scale extends number | undefined = undefined,
|
|
68
|
+
>(
|
|
69
|
+
precision?: Precision,
|
|
70
|
+
scale?: Scale,
|
|
71
|
+
) => new DecimalBigIntColumn(precision, scale),
|
|
72
|
+
decimal: <
|
|
73
|
+
Precision extends number | undefined = undefined,
|
|
74
|
+
Scale extends number | undefined = undefined,
|
|
75
|
+
>(
|
|
76
|
+
precision?: Precision,
|
|
77
|
+
scale?: Scale,
|
|
78
|
+
) => new DecimalColumn(precision, scale),
|
|
79
|
+
decimalBigInt: <
|
|
80
|
+
Precision extends number | undefined = undefined,
|
|
81
|
+
Scale extends number | undefined = undefined,
|
|
82
|
+
>(
|
|
83
|
+
precision?: Precision,
|
|
84
|
+
scale?: Scale,
|
|
85
|
+
) => new DecimalBigIntColumn(precision, scale),
|
|
86
|
+
real: () => new RealColumn(),
|
|
87
|
+
doublePrecision: () => new DoublePrecisionColumn(),
|
|
88
|
+
smallSerial: () => new SmallSerialColumn(),
|
|
89
|
+
serial: () => new SerialColumn(),
|
|
90
|
+
bigserial: () => new BigSerialColumn(),
|
|
91
|
+
money: () => new MoneyColumn(),
|
|
92
|
+
varchar: <Limit extends number | undefined = undefined>(limit?: Limit) =>
|
|
93
|
+
new VarCharColumn(limit),
|
|
94
|
+
char: <Limit extends number | undefined = undefined>(limit?: Limit) =>
|
|
95
|
+
new CharColumn(limit),
|
|
96
|
+
text: () => new TextColumn(),
|
|
97
|
+
bytea: () => new ByteaColumn(),
|
|
98
|
+
date: () => new DateColumn(),
|
|
99
|
+
timestamp: <Precision extends number | undefined = undefined>(
|
|
100
|
+
precision?: Precision,
|
|
101
|
+
) => new TimestampColumn(precision),
|
|
102
|
+
timestampWithTimeZone: <Precision extends number | undefined = undefined>(
|
|
103
|
+
precision?: Precision,
|
|
104
|
+
) => new TimestampWithTimeZoneColumn(precision),
|
|
105
|
+
time: <Precision extends number | undefined = undefined>(
|
|
106
|
+
precision?: Precision,
|
|
107
|
+
) => new TimeColumn(precision),
|
|
108
|
+
timeWithTimeZone: <Precision extends number | undefined = undefined>(
|
|
109
|
+
precision?: Precision,
|
|
110
|
+
) => new TimeWithTimeZoneColumn(precision),
|
|
111
|
+
interval: <
|
|
112
|
+
Fields extends string | undefined = undefined,
|
|
113
|
+
Precision extends number | undefined = undefined,
|
|
114
|
+
>(
|
|
115
|
+
fields?: Fields,
|
|
116
|
+
precision?: Precision,
|
|
117
|
+
) => new IntervalColumn(fields, precision),
|
|
118
|
+
boolean: () => new BooleanColumn(),
|
|
119
|
+
enum: <DataType extends string, Type>(dataType: DataType) =>
|
|
120
|
+
new EnumColumn<DataType, Type>(dataType),
|
|
121
|
+
point: () => new PointColumn(),
|
|
122
|
+
line: () => new LineColumn(),
|
|
123
|
+
lseg: () => new LsegColumn(),
|
|
124
|
+
box: () => new BoxColumn(),
|
|
125
|
+
path: () => new PathColumn(),
|
|
126
|
+
polygon: () => new PolygonColumn(),
|
|
127
|
+
circle: () => new CircleColumn(),
|
|
128
|
+
cidr() {
|
|
129
|
+
return new CidrColumn();
|
|
130
|
+
},
|
|
131
|
+
inet: () => new InetColumn(),
|
|
132
|
+
macaddr: () => new MacAddrColumn(),
|
|
133
|
+
macaddr8: () => new MacAddr8Column(),
|
|
134
|
+
bit: () => new BitColumn(),
|
|
135
|
+
bitVarying: () => new BitVaryingColumn(),
|
|
136
|
+
tsvector: () => new TsVectorColumn(),
|
|
137
|
+
tsquery: () => new TsQueryColumn(),
|
|
138
|
+
uuid: () => new UUIDColumn(),
|
|
139
|
+
xml: () => new XMLColumn(),
|
|
140
|
+
json: <Type extends JSONTypeAny>(
|
|
141
|
+
schemaOrFn: Type | ((j: JSONTypes) => Type),
|
|
142
|
+
) => new JSONColumn(schemaOrFn),
|
|
143
|
+
jsonText: () => new JSONTextColumn(),
|
|
144
|
+
array: <Item extends ColumnType>(item: Item) => new ArrayColumn(item),
|
|
145
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AssertEqual } from '../test-utils';
|
|
2
|
+
import { TableSchema } from './columnsSchema';
|
|
3
|
+
import { columnTypes } from './columnTypes';
|
|
4
|
+
|
|
5
|
+
describe('columnsSchema', () => {
|
|
6
|
+
describe('schema methods', () => {
|
|
7
|
+
const createSchema = () => {
|
|
8
|
+
return new TableSchema({
|
|
9
|
+
a: columnTypes.integer().primaryKey(),
|
|
10
|
+
b: columnTypes.decimal().primaryKey(),
|
|
11
|
+
c: columnTypes.text(),
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('.primaryKeys', () => {
|
|
16
|
+
it('should be array of primary key names', () => {
|
|
17
|
+
const schema = createSchema();
|
|
18
|
+
const eq: AssertEqual<typeof schema.primaryKeys, ['a', 'b']> = true;
|
|
19
|
+
expect(eq).toBe(true);
|
|
20
|
+
expect(schema.primaryKeys).toEqual(['a', 'b']);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('.primaryTypes', () => {
|
|
25
|
+
const schema = createSchema();
|
|
26
|
+
const eq: AssertEqual<typeof schema.primaryTypes, [number, number]> =
|
|
27
|
+
true;
|
|
28
|
+
expect(eq).toBe(true);
|
|
29
|
+
expect(schema.primaryTypes).toEqual(undefined);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|