pqb 0.7.0 → 0.7.2
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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +45 -9
- package/dist/index.esm.js +73 -6
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +73 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter.test.ts +1 -1
- package/src/columnSchema/columnType.test.ts +2 -1
- package/src/columnSchema/columnTypes.test.ts +1 -1
- package/src/columnSchema/timestamps.test.ts +2 -2
- package/src/columnSchema/utils.ts +1 -1
- package/src/columnsOperators.test.ts +1 -1
- package/src/db.test.ts +83 -1
- package/src/db.ts +27 -10
- package/src/errors.test.ts +6 -1
- package/src/queryMethods/aggregate.test.ts +1 -1
- package/src/queryMethods/callbacks.test.ts +1 -1
- package/src/queryMethods/clear.test.ts +1 -1
- package/src/queryMethods/columnInfo.test.ts +5 -1
- package/src/queryMethods/copy.test.ts +63 -0
- package/src/queryMethods/copy.ts +17 -0
- package/src/queryMethods/create.test.ts +1 -1
- package/src/queryMethods/delete.test.ts +1 -1
- package/src/queryMethods/for.test.ts +5 -1
- package/src/queryMethods/from.test.ts +6 -1
- package/src/queryMethods/get.test.ts +7 -1
- package/src/queryMethods/having.test.ts +6 -1
- package/src/queryMethods/join.test.ts +6 -1
- package/src/queryMethods/json.test.ts +1 -1
- package/src/queryMethods/log.test.ts +6 -1
- package/src/queryMethods/log.ts +1 -0
- package/src/queryMethods/merge.test.ts +1 -1
- package/src/queryMethods/queryMethods.test.ts +1 -1
- package/src/queryMethods/queryMethods.ts +4 -1
- package/src/queryMethods/raw.test.ts +1 -1
- package/src/queryMethods/select.test.ts +1 -1
- package/src/queryMethods/then.test.ts +6 -1
- package/src/queryMethods/transaction.test.ts +1 -1
- package/src/queryMethods/union.test.ts +1 -1
- package/src/queryMethods/update.test.ts +1 -1
- package/src/queryMethods/upsert.test.ts +6 -1
- package/src/queryMethods/where.test.ts +1 -1
- package/src/queryMethods/window.test.ts +1 -1
- package/src/queryMethods/with.test.ts +6 -1
- package/src/sql/copy.ts +59 -0
- package/src/sql/data.ts +29 -1
- package/src/sql/toSql.ts +6 -0
- package/src/{test-utils.ts → test-utils/test-utils.ts} +8 -7
- package/src/test-utils/user.csv +1 -0
- package/src/utils.test.ts +1 -1
package/package.json
CHANGED
package/src/adapter.test.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
User,
|
|
9
9
|
userData,
|
|
10
10
|
useTestDatabase,
|
|
11
|
-
} from '../test-utils';
|
|
11
|
+
} from '../test-utils/test-utils';
|
|
12
12
|
import { createDb } from '../db';
|
|
13
13
|
import { columnTypes } from './columnTypes';
|
|
14
14
|
|
|
@@ -109,6 +109,7 @@ describe('column base', () => {
|
|
|
109
109
|
const db = createDb({ adapter, columnTypes });
|
|
110
110
|
|
|
111
111
|
const UserWithPlainTimestamp = db('user', (t) => ({
|
|
112
|
+
id: t.serial().primaryKey(),
|
|
112
113
|
createdAt: t.timestamp(),
|
|
113
114
|
}));
|
|
114
115
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { db, expectSql, now, useTestDatabase } from '../test-utils';
|
|
1
|
+
import { db, expectSql, now, useTestDatabase } from '../test-utils/test-utils';
|
|
2
2
|
|
|
3
3
|
describe('timestamps', () => {
|
|
4
4
|
useTestDatabase();
|
|
5
5
|
|
|
6
6
|
const model = db('user', (t) => ({
|
|
7
|
-
name: t.string(),
|
|
7
|
+
name: t.string().primaryKey(),
|
|
8
8
|
...t.timestamps(),
|
|
9
9
|
}));
|
|
10
10
|
|
|
@@ -18,7 +18,7 @@ export const assignMethodsToClass = <Methods extends Record<string, unknown>>(
|
|
|
18
18
|
const cloned = cloneInstance(this);
|
|
19
19
|
return (
|
|
20
20
|
methods as unknown as Record<string, (...args: unknown[]) => unknown>
|
|
21
|
-
)[name].
|
|
21
|
+
)[name].apply(cloned, args);
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
24
|
}
|
package/src/db.test.ts
CHANGED
|
@@ -6,9 +6,10 @@ import {
|
|
|
6
6
|
User,
|
|
7
7
|
userData,
|
|
8
8
|
useTestDatabase,
|
|
9
|
-
} from './test-utils';
|
|
9
|
+
} from './test-utils/test-utils';
|
|
10
10
|
import { createDb } from './db';
|
|
11
11
|
import { columnTypes } from './columnSchema';
|
|
12
|
+
import { QueryLogger } from './queryMethods';
|
|
12
13
|
|
|
13
14
|
describe('db', () => {
|
|
14
15
|
useTestDatabase();
|
|
@@ -26,12 +27,30 @@ describe('db', () => {
|
|
|
26
27
|
);
|
|
27
28
|
});
|
|
28
29
|
|
|
30
|
+
describe('primaryKeys', () => {
|
|
31
|
+
it('should collect primary keys from schema', () => {
|
|
32
|
+
const table = db('table', (t) => ({
|
|
33
|
+
id: t.serial().primaryKey(),
|
|
34
|
+
name: t.text().primaryKey(),
|
|
35
|
+
}));
|
|
36
|
+
expect(table.primaryKeys).toEqual(['id', 'name']);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should set primary keys from primaryKey in schema', () => {
|
|
40
|
+
const table = db('table', (t) => ({
|
|
41
|
+
...t.primaryKey(['id', 'name']),
|
|
42
|
+
}));
|
|
43
|
+
expect(table.primaryKeys).toEqual(['id', 'name']);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
29
47
|
describe('overriding column types', () => {
|
|
30
48
|
it('should return date as string by default', async () => {
|
|
31
49
|
await User.create(userData);
|
|
32
50
|
|
|
33
51
|
const db = createDb({ adapter, columnTypes });
|
|
34
52
|
const table = db('user', (t) => ({
|
|
53
|
+
id: t.serial().primaryKey(),
|
|
35
54
|
createdAt: t.timestamp(),
|
|
36
55
|
}));
|
|
37
56
|
|
|
@@ -47,12 +66,15 @@ describe('db', () => {
|
|
|
47
66
|
const db = createDb({
|
|
48
67
|
adapter,
|
|
49
68
|
columnTypes: {
|
|
69
|
+
serial: columnTypes.serial,
|
|
50
70
|
timestamp() {
|
|
51
71
|
return columnTypes.timestamp().parse((input) => new Date(input));
|
|
52
72
|
},
|
|
53
73
|
},
|
|
54
74
|
});
|
|
75
|
+
|
|
55
76
|
const table = db('user', (t) => ({
|
|
77
|
+
id: t.serial().primaryKey(),
|
|
56
78
|
createdAt: t.timestamp(),
|
|
57
79
|
}));
|
|
58
80
|
|
|
@@ -71,4 +93,64 @@ describe('db', () => {
|
|
|
71
93
|
expect(table.query.autoPreparedStatements).toBe(false);
|
|
72
94
|
});
|
|
73
95
|
});
|
|
96
|
+
|
|
97
|
+
describe('noPrimaryKey', () => {
|
|
98
|
+
it('should throw error when no primary key by default', () => {
|
|
99
|
+
const db = createDb({ adapter, columnTypes });
|
|
100
|
+
|
|
101
|
+
expect(() =>
|
|
102
|
+
db('table', (t) => ({
|
|
103
|
+
name: t.text(),
|
|
104
|
+
})),
|
|
105
|
+
).toThrow(`Table table has no primary key`);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should throw error when no primary key when noPrimaryKey is set to `error`', () => {
|
|
109
|
+
const db = createDb({ adapter, columnTypes, noPrimaryKey: 'error' });
|
|
110
|
+
|
|
111
|
+
expect(() =>
|
|
112
|
+
db('table', (t) => ({
|
|
113
|
+
name: t.text(),
|
|
114
|
+
})),
|
|
115
|
+
).toThrow(`Table table has no primary key`);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should not throw when no column shape is provided', () => {
|
|
119
|
+
const db = createDb({ adapter, columnTypes });
|
|
120
|
+
|
|
121
|
+
expect(() => db('table')).not.toThrow();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should warn when no primary key and noPrimaryKey is set to `warning`', () => {
|
|
125
|
+
const logger = { warn: jest.fn() };
|
|
126
|
+
const db = createDb({
|
|
127
|
+
adapter,
|
|
128
|
+
columnTypes,
|
|
129
|
+
noPrimaryKey: 'warning',
|
|
130
|
+
logger: logger as unknown as QueryLogger,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
db('table', (t) => ({
|
|
134
|
+
name: t.text(),
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
expect(logger.warn).toBeCalledWith('Table table has no primary key');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should do nothing when no primary key and noPrimaryKey is set to `ignore`', () => {
|
|
141
|
+
const logger = { warn: jest.fn() };
|
|
142
|
+
const db = createDb({
|
|
143
|
+
adapter,
|
|
144
|
+
columnTypes,
|
|
145
|
+
noPrimaryKey: 'ignore',
|
|
146
|
+
logger: logger as unknown as QueryLogger,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
db('table', (t) => ({
|
|
150
|
+
name: t.text(),
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
expect(logger.warn).not.toBeCalled();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
74
156
|
});
|
package/src/db.ts
CHANGED
|
@@ -23,16 +23,30 @@ import {
|
|
|
23
23
|
getColumnTypes,
|
|
24
24
|
SinglePrimaryKey,
|
|
25
25
|
ColumnType,
|
|
26
|
+
getTableData,
|
|
26
27
|
} from './columnSchema';
|
|
27
28
|
import { applyMixins, pushOrNewArray } from './utils';
|
|
28
29
|
import { StringKey } from './common';
|
|
29
30
|
import { QueryError, QueryErrorName } from './errors';
|
|
30
31
|
|
|
32
|
+
export type NoPrimaryKeyOption = 'error' | 'warning' | 'ignore';
|
|
33
|
+
|
|
34
|
+
export type DbOptions<CT extends ColumnTypesBase> = (
|
|
35
|
+
| { adapter: Adapter }
|
|
36
|
+
| Omit<AdapterOptions, 'log'>
|
|
37
|
+
) &
|
|
38
|
+
QueryLogOptions & {
|
|
39
|
+
columnTypes: CT;
|
|
40
|
+
autoPreparedStatements?: boolean;
|
|
41
|
+
noPrimaryKey?: NoPrimaryKeyOption;
|
|
42
|
+
};
|
|
43
|
+
|
|
31
44
|
export type DbTableOptions = {
|
|
32
45
|
schema?: string;
|
|
33
46
|
// prepare all SQL queries before executing
|
|
34
47
|
// true by default
|
|
35
48
|
autoPreparedStatements?: boolean;
|
|
49
|
+
noPrimaryKey?: NoPrimaryKeyOption;
|
|
36
50
|
} & QueryLogOptions;
|
|
37
51
|
|
|
38
52
|
export interface Db<
|
|
@@ -136,9 +150,20 @@ export class Db<
|
|
|
136
150
|
this.primaryKeys = Object.keys(shape).filter(
|
|
137
151
|
(key) => shape[key].isPrimaryKey,
|
|
138
152
|
);
|
|
153
|
+
const primaryKeysFromData = getTableData().primaryKey?.columns;
|
|
154
|
+
if (primaryKeysFromData) this.primaryKeys.push(...primaryKeysFromData);
|
|
155
|
+
|
|
139
156
|
if (this.primaryKeys.length === 1) {
|
|
140
157
|
this.singlePrimaryKey = this
|
|
141
158
|
.primaryKeys[0] as unknown as SinglePrimaryKey<Shape>;
|
|
159
|
+
} else if (
|
|
160
|
+
this.primaryKeys.length === 0 &&
|
|
161
|
+
shape !== anyShape &&
|
|
162
|
+
options.noPrimaryKey !== 'ignore'
|
|
163
|
+
) {
|
|
164
|
+
const message = `Table ${table} has no primary key`;
|
|
165
|
+
if (options.noPrimaryKey === 'error') throw new Error(message);
|
|
166
|
+
else logger.warn(message);
|
|
142
167
|
}
|
|
143
168
|
|
|
144
169
|
const columns = Object.keys(
|
|
@@ -209,15 +234,6 @@ type DbResult<CT extends ColumnTypesBase> = Db<
|
|
|
209
234
|
close: Adapter['close'];
|
|
210
235
|
};
|
|
211
236
|
|
|
212
|
-
export type DbOptions<CT extends ColumnTypesBase> = (
|
|
213
|
-
| { adapter: Adapter }
|
|
214
|
-
| Omit<AdapterOptions, 'log'>
|
|
215
|
-
) &
|
|
216
|
-
QueryLogOptions & {
|
|
217
|
-
columnTypes: CT;
|
|
218
|
-
autoPreparedStatements?: boolean;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
237
|
export const createDb = <CT extends ColumnTypesBase>({
|
|
222
238
|
log,
|
|
223
239
|
logger,
|
|
@@ -229,13 +245,14 @@ export const createDb = <CT extends ColumnTypesBase>({
|
|
|
229
245
|
log,
|
|
230
246
|
logger,
|
|
231
247
|
autoPreparedStatements: options.autoPreparedStatements ?? false,
|
|
248
|
+
noPrimaryKey: options.noPrimaryKey ?? 'error',
|
|
232
249
|
};
|
|
233
250
|
|
|
234
251
|
const qb = new Db(
|
|
235
252
|
adapter,
|
|
236
253
|
undefined as unknown as Db,
|
|
237
254
|
undefined,
|
|
238
|
-
|
|
255
|
+
anyShape,
|
|
239
256
|
ct,
|
|
240
257
|
commonOptions,
|
|
241
258
|
);
|
package/src/errors.test.ts
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { expectSql, User, useTestDatabase } from '../test-utils/test-utils';
|
|
2
|
+
|
|
3
|
+
describe('copy', () => {
|
|
4
|
+
useTestDatabase();
|
|
5
|
+
|
|
6
|
+
const columns = ['name', 'password'] as ['name', 'password'];
|
|
7
|
+
const options = {
|
|
8
|
+
columns: columns,
|
|
9
|
+
format: 'csv' as const,
|
|
10
|
+
freeze: true,
|
|
11
|
+
delimiter: ',',
|
|
12
|
+
null: 'null',
|
|
13
|
+
header: 'match' as const,
|
|
14
|
+
quote: 'quote',
|
|
15
|
+
escape: 'escape',
|
|
16
|
+
forceQuote: columns,
|
|
17
|
+
forceNotNull: columns,
|
|
18
|
+
forceNull: columns,
|
|
19
|
+
encoding: 'encoding',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe.each`
|
|
23
|
+
method | sql
|
|
24
|
+
${'from'} | ${'FROM'}
|
|
25
|
+
${'to'} | ${'TO'}
|
|
26
|
+
`('$method', ({ method, sql }) => {
|
|
27
|
+
test(`simple copy ${method}`, () => {
|
|
28
|
+
const q = User.copy({
|
|
29
|
+
[method as 'from']: 'path-to-file',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expectSql(q.toSql(), `COPY "user" ${sql} 'path-to-file'`);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test(`copy ${method} with options`, () => {
|
|
36
|
+
const q = User.copy({
|
|
37
|
+
[method as 'from']: { program: 'program' },
|
|
38
|
+
...options,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expectSql(
|
|
42
|
+
q.toSql(),
|
|
43
|
+
`
|
|
44
|
+
COPY "user"("name", "password")
|
|
45
|
+
${sql} PROGRAM 'program'
|
|
46
|
+
WITH (
|
|
47
|
+
FORMAT csv,
|
|
48
|
+
FREEZE true,
|
|
49
|
+
DELIMITER ',',
|
|
50
|
+
NULL 'null',
|
|
51
|
+
HEADER match,
|
|
52
|
+
QUOTE 'quote',
|
|
53
|
+
ESCAPE 'escape',
|
|
54
|
+
FORCE_QUOTE ("name", "password"),
|
|
55
|
+
FORCE_NOT_NULL ("name", "password"),
|
|
56
|
+
FORCE_NULL ("name", "password"),
|
|
57
|
+
ENCODING 'encoding'
|
|
58
|
+
)
|
|
59
|
+
`,
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Query } from '../query';
|
|
2
|
+
import { CopyOptions } from '../sql';
|
|
3
|
+
|
|
4
|
+
type CopyArg<T extends Query> = CopyOptions<keyof T['shape']>;
|
|
5
|
+
|
|
6
|
+
export class CopyMethods {
|
|
7
|
+
copy<T extends Query>(this: T, arg: CopyArg<T>): T {
|
|
8
|
+
return this.clone()._copy(arg);
|
|
9
|
+
}
|
|
10
|
+
_copy<T extends Query>(this: T, arg: CopyArg<T>) {
|
|
11
|
+
Object.assign(this.query, {
|
|
12
|
+
type: 'copy',
|
|
13
|
+
copy: arg,
|
|
14
|
+
});
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
assertType,
|
|
3
|
+
db,
|
|
4
|
+
User,
|
|
5
|
+
userData,
|
|
6
|
+
useTestDatabase,
|
|
7
|
+
} from '../test-utils/test-utils';
|
|
2
8
|
import { NotFoundError } from '../errors';
|
|
3
9
|
|
|
4
10
|
describe('get', () => {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
expectQueryNotMutated,
|
|
3
|
+
expectSql,
|
|
4
|
+
Message,
|
|
5
|
+
User,
|
|
6
|
+
} from '../test-utils/test-utils';
|
|
2
7
|
import { OnQueryBuilder } from './join';
|
|
3
8
|
import { testJoin, testWhere } from './where.test';
|
|
4
9
|
import { Sql } from '../sql';
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { createDb } from '../db';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
adapter,
|
|
4
|
+
dbOptions,
|
|
5
|
+
userData,
|
|
6
|
+
useTestDatabase,
|
|
7
|
+
} from '../test-utils/test-utils';
|
|
3
8
|
import { logColors } from './log';
|
|
4
9
|
import { noop } from '../utils';
|
|
5
10
|
import { columnTypes } from '../columnSchema';
|
package/src/queryMethods/log.ts
CHANGED
|
@@ -49,6 +49,7 @@ import { QueryUpsert } from './upsert';
|
|
|
49
49
|
import { QueryGet } from './get';
|
|
50
50
|
import { MergeQueryMethods } from './merge';
|
|
51
51
|
import { RawMethods } from './raw';
|
|
52
|
+
import { CopyMethods } from './copy';
|
|
52
53
|
|
|
53
54
|
export type WindowArg<T extends Query> = Record<
|
|
54
55
|
string,
|
|
@@ -98,7 +99,8 @@ export interface QueryMethods
|
|
|
98
99
|
QueryUpsert,
|
|
99
100
|
QueryGet,
|
|
100
101
|
MergeQueryMethods,
|
|
101
|
-
RawMethods
|
|
102
|
+
RawMethods,
|
|
103
|
+
CopyMethods {}
|
|
102
104
|
|
|
103
105
|
export class QueryMethods {
|
|
104
106
|
windows!: EmptyObject;
|
|
@@ -394,4 +396,5 @@ applyMixins(QueryMethods, [
|
|
|
394
396
|
QueryGet,
|
|
395
397
|
MergeQueryMethods,
|
|
396
398
|
RawMethods,
|
|
399
|
+
CopyMethods,
|
|
397
400
|
]);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ColumnType } from '../columnSchema';
|
|
2
2
|
import { createDb } from '../db';
|
|
3
|
-
import { adapter } from '../test-utils';
|
|
3
|
+
import { adapter } from '../test-utils/test-utils';
|
|
4
4
|
|
|
5
5
|
describe('raw', () => {
|
|
6
6
|
it('should use column types in callback from a db instance', () => {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { WithOptions } from '../sql';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
db,
|
|
4
|
+
expectQueryNotMutated,
|
|
5
|
+
expectSql,
|
|
6
|
+
User,
|
|
7
|
+
} from '../test-utils/test-utils';
|
|
3
8
|
import { columnTypes } from '../columnSchema';
|
|
4
9
|
|
|
5
10
|
describe('with', () => {
|