@squiz/db-lib 1.2.1-alpha.95 → 1.2.1-alpha.97
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +16 -0
- package/lib/AbstractRepository.d.ts +5 -3
- package/lib/PostgresErrorCodes.d.ts +268 -0
- package/lib/assertions/assertAssign.d.ts +4 -0
- package/lib/assertions/assertAssign.spec.d.ts +1 -0
- package/lib/assertions/assertAssignWithDefaultUndefinedValue.d.ts +4 -0
- package/lib/assertions/assertAssignWithDefaultUndefinedValue.spec.d.ts +1 -0
- package/lib/assertions/assertIsDefined.d.ts +1 -0
- package/lib/assertions/assertIsDefined.spec.d.ts +1 -0
- package/lib/assertions/assertIsMapOfStringString.d.ts +3 -0
- package/lib/assertions/assertIsMapOfStringString.spec.d.ts +1 -0
- package/lib/assertions/assertIsNotAnEmptyString.d.ts +1 -0
- package/lib/assertions/assertIsNotAnEmptyString.spec.d.ts +1 -0
- package/lib/assertions/assertIsObject.d.ts +1 -0
- package/lib/assertions/assertIsObject.spec.d.ts +1 -0
- package/lib/assertions/assertIsString.d.ts +1 -0
- package/lib/assertions/assertIsString.spec.d.ts +1 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +12369 -1364
- package/lib/index.js.map +4 -4
- package/package.json +10 -9
- package/src/AbstractRepository.ts +11 -7
- package/src/Migrator.ts +13 -9
- package/src/PostgresErrorCodes.ts +270 -0
- package/src/assertions/assertAssign.spec.ts +18 -0
- package/src/assertions/assertAssign.ts +22 -0
- package/src/assertions/assertAssignWithDefaultUndefinedValue.spec.ts +31 -0
- package/src/assertions/assertAssignWithDefaultUndefinedValue.ts +17 -0
- package/src/assertions/assertIsDefined.spec.ts +19 -0
- package/src/assertions/assertIsDefined.ts +7 -0
- package/src/assertions/assertIsMapOfStringString.spec.ts +23 -0
- package/src/assertions/assertIsMapOfStringString.ts +29 -0
- package/src/assertions/assertIsNotAnEmptyString.spec.ts +25 -0
- package/src/assertions/assertIsNotAnEmptyString.ts +17 -0
- package/src/assertions/assertIsObject.spec.ts +17 -0
- package/src/assertions/assertIsObject.ts +11 -0
- package/src/assertions/assertIsString.spec.ts +16 -0
- package/src/assertions/assertIsString.ts +11 -0
- package/src/index.ts +14 -0
- package/tsconfig.json +5 -1
- package/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/db-lib",
|
3
|
-
"version": "1.2.1-alpha.
|
3
|
+
"version": "1.2.1-alpha.97",
|
4
4
|
"description": "",
|
5
5
|
"main": "lib/index.js",
|
6
6
|
"scripts": {
|
7
7
|
"start": "node ./lib/index.js",
|
8
8
|
"compile": "node build.js && npx tsc",
|
9
9
|
"lint": "eslint ./src --ext .ts",
|
10
|
-
"test": "
|
10
|
+
"test": "jest -c jest.config.ts",
|
11
11
|
"test:integration___": "jest -c jest.config.ts",
|
12
12
|
"test:update-snapshots": "jest -c jest.config.ts --updateSnapshot",
|
13
13
|
"clean": "rimraf \".tsbuildinfo\" \"./lib\""
|
@@ -18,20 +18,21 @@
|
|
18
18
|
"@types/jest": "28.1.6",
|
19
19
|
"@types/node": "17.0.27",
|
20
20
|
"@types/pg": "8.6.5",
|
21
|
-
"esbuild": "0.
|
22
|
-
"eslint": "8.
|
21
|
+
"esbuild": "0.15.3",
|
22
|
+
"eslint": "8.22.0",
|
23
23
|
"fs-extra": "10.1.0",
|
24
24
|
"jest": "28.1.3",
|
25
25
|
"rimraf": "3.0.2",
|
26
|
-
"ts-jest": "28.0.
|
26
|
+
"ts-jest": "28.0.7",
|
27
27
|
"ts-loader": "9.3.1",
|
28
|
-
"ts-node": "10.
|
28
|
+
"ts-node": "10.9.1",
|
29
29
|
"typescript": "4.7.4"
|
30
30
|
},
|
31
31
|
"dependencies": {
|
32
|
-
"@aws-sdk/client-secrets-manager": "
|
32
|
+
"@aws-sdk/client-secrets-manager": "3.121.0",
|
33
|
+
"@squiz/dx-logger-lib": "^1.2.1-alpha.97",
|
33
34
|
"dotenv": "16.0.1",
|
34
|
-
"pg": "
|
35
|
+
"pg": "8.7.3"
|
35
36
|
},
|
36
|
-
"gitHead": "
|
37
|
+
"gitHead": "bf3e5580c5b09c3e46a5965da38e4ed4657463d4"
|
37
38
|
}
|
@@ -17,24 +17,27 @@ export type Repository<T> = Reader<T> & Writer<T>;
|
|
17
17
|
export abstract class AbstractRepository<T> implements Reader<T>, Writer<T> {
|
18
18
|
protected tableName: string;
|
19
19
|
|
20
|
+
/** object where the key is the model property name amd the value is sql column name */
|
20
21
|
protected modelPropertyToSqlColumn: { [key in keyof T]: string };
|
21
|
-
|
22
|
+
/** object where the key is the sql column name and the value is the model property name */
|
23
|
+
protected sqlColumnToModelProperty: { [key: string]: string };
|
22
24
|
|
23
25
|
constructor(
|
24
26
|
protected repositories: Repositories,
|
25
27
|
protected pool: Pool,
|
26
28
|
tableName: string,
|
27
29
|
mapping: { [key in keyof T]: string },
|
28
|
-
protected classRef: { new (): T },
|
30
|
+
protected classRef: { new (data?: Record<string, unknown>): T },
|
29
31
|
) {
|
30
32
|
this.tableName = `"${tableName}"`;
|
31
33
|
|
32
34
|
this.modelPropertyToSqlColumn = mapping;
|
35
|
+
|
33
36
|
this.sqlColumnToModelProperty = Object.entries(mapping).reduce((prev, curr) => {
|
34
|
-
const [modelProp, columnName] = curr as [
|
37
|
+
const [modelProp, columnName] = curr as [string, string];
|
35
38
|
prev[columnName] = modelProp;
|
36
39
|
return prev;
|
37
|
-
}, {} as { [key: string]:
|
40
|
+
}, {} as { [key: string]: string });
|
38
41
|
}
|
39
42
|
|
40
43
|
protected async getConnection(): Promise<PoolClient> {
|
@@ -131,13 +134,14 @@ export abstract class AbstractRepository<T> implements Reader<T>, Writer<T> {
|
|
131
134
|
}
|
132
135
|
|
133
136
|
protected createAndHydrateModel(row: any): T {
|
134
|
-
const
|
137
|
+
const inputData: Record<string, unknown> = {};
|
135
138
|
|
136
139
|
for (const key of Object.keys(row)) {
|
137
|
-
|
140
|
+
const translatedKey = this.sqlColumnToModelProperty[key];
|
141
|
+
inputData[translatedKey] = row[key];
|
138
142
|
}
|
139
143
|
|
140
|
-
return
|
144
|
+
return new this.classRef(inputData);
|
141
145
|
}
|
142
146
|
|
143
147
|
async findOne(item: Partial<T>): Promise<T | undefined> {
|
package/src/Migrator.ts
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
import fs from 'fs/promises';
|
2
2
|
import { PoolClient } from 'pg';
|
3
3
|
import path from 'path';
|
4
|
+
import os from 'os';
|
5
|
+
import { getLogger } from '@squiz/dx-logger-lib';
|
4
6
|
|
5
7
|
// Be carful about changing this value. This needs to be consistent across deployments
|
6
8
|
// this ID is used to signal other running instances that a migration is executing.
|
7
9
|
const MIGRATION_ADVISORY_LOCK = 4569465;
|
8
10
|
|
11
|
+
const logger = getLogger({ name: 'db-migrator', meta: { pid: process.pid, hostname: os.hostname() } });
|
12
|
+
|
9
13
|
export class Migrator {
|
10
14
|
constructor(protected migrationDir: string, protected migrationList: string[], protected pool: PoolClient) {}
|
11
15
|
|
@@ -25,15 +29,15 @@ export class Migrator {
|
|
25
29
|
protected async applyMigration(migration: string, sql: string) {
|
26
30
|
try {
|
27
31
|
const result = await this.pool.query(sql);
|
28
|
-
|
32
|
+
logger.info('Applying ' + migration);
|
29
33
|
|
30
34
|
if (result.rowCount !== undefined) {
|
31
|
-
|
35
|
+
logger.info('affected rows', result.rowCount);
|
32
36
|
}
|
33
37
|
|
34
38
|
await this.pool.query('insert into __migrations__ (id) values ($1)', [migration]);
|
35
39
|
} catch (e) {
|
36
|
-
|
40
|
+
logger.info('error occurred running migration', migration, e);
|
37
41
|
throw e;
|
38
42
|
}
|
39
43
|
}
|
@@ -79,18 +83,18 @@ export class Migrator {
|
|
79
83
|
const lockObtained = await this.tryToObtainLock();
|
80
84
|
|
81
85
|
if (lockObtained === false) {
|
82
|
-
|
86
|
+
logger.info('migration already running');
|
83
87
|
await sleep(500);
|
84
88
|
return await this.migrate();
|
85
89
|
}
|
86
90
|
|
87
91
|
await this.runMigrations();
|
88
|
-
|
92
|
+
logger.info('completed migration');
|
89
93
|
await this.releaseLock();
|
90
94
|
|
91
95
|
this.dispose();
|
92
96
|
} catch (e) {
|
93
|
-
|
97
|
+
logger.info('migration failed releasing lock');
|
94
98
|
await this.releaseLock();
|
95
99
|
throw e;
|
96
100
|
}
|
@@ -101,11 +105,11 @@ export class Migrator {
|
|
101
105
|
const pending = await this.getPending(this.migrationList, appliedMigrations);
|
102
106
|
|
103
107
|
if (pending.length === 0) {
|
104
|
-
|
108
|
+
logger.info('No pending migrations');
|
105
109
|
return;
|
106
110
|
}
|
107
111
|
|
108
|
-
|
112
|
+
logger.info('Pending migrations:\n\t', pending.join('\n\t'));
|
109
113
|
|
110
114
|
for (const migration of pending) {
|
111
115
|
await this.runMigration(migration);
|
@@ -121,7 +125,7 @@ export class Migrator {
|
|
121
125
|
|
122
126
|
await this.pool.query('COMMIT');
|
123
127
|
} catch (e) {
|
124
|
-
|
128
|
+
logger.error('migration failed', migration, e);
|
125
129
|
await this.pool.query('ROLLBACK');
|
126
130
|
throw e;
|
127
131
|
}
|
@@ -0,0 +1,270 @@
|
|
1
|
+
// See https://github.com/postgres/postgres/blob/master/src/backend/utils/errcodes.txt
|
2
|
+
|
3
|
+
export enum PostgresErrorCode {
|
4
|
+
SUCCESSFUL_COMPLETION = '00000',
|
5
|
+
WARNING = '01000',
|
6
|
+
WARNING_DYNAMIC_RESULT_SETS_RETURNED = '0100C',
|
7
|
+
WARNING_IMPLICIT_ZERO_BIT_PADDING = '01008',
|
8
|
+
WARNING_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION = '01003',
|
9
|
+
WARNING_PRIVILEGE_NOT_GRANTED = '01007',
|
10
|
+
WARNING_PRIVILEGE_NOT_REVOKED = '01006',
|
11
|
+
WARNING_STRING_DATA_RIGHT_TRUNCATION = '01004',
|
12
|
+
WARNING_DEPRECATED_FEATURE = '01P01',
|
13
|
+
NO_DATA = '02000',
|
14
|
+
NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED = '02001',
|
15
|
+
SQL_STATEMENT_NOT_YET_COMPLETE = '03000',
|
16
|
+
CONNECTION_EXCEPTION = '08000',
|
17
|
+
CONNECTION_DOES_NOT_EXIST = '08003',
|
18
|
+
CONNECTION_FAILURE = '08006',
|
19
|
+
SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION = '08001',
|
20
|
+
SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION = '08004',
|
21
|
+
TRANSACTION_RESOLUTION_UNKNOWN = '08007',
|
22
|
+
PROTOCOL_VIOLATION = '08P01',
|
23
|
+
TRIGGERED_ACTION_EXCEPTION = '09000',
|
24
|
+
FEATURE_NOT_SUPPORTED = '0A000',
|
25
|
+
INVALID_TRANSACTION_INITIATION = '0B000',
|
26
|
+
LOCATOR_EXCEPTION = '0F000',
|
27
|
+
L_E_INVALID_SPECIFICATION = '0F001',
|
28
|
+
INVALID_GRANTOR = '0L000',
|
29
|
+
INVALID_GRANT_OPERATION = '0LP01',
|
30
|
+
INVALID_ROLE_SPECIFICATION = '0P000',
|
31
|
+
DIAGNOSTICS_EXCEPTION = '0Z000',
|
32
|
+
STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER = '0Z002',
|
33
|
+
CASE_NOT_FOUND = '20000',
|
34
|
+
CARDINALITY_VIOLATION = '21000',
|
35
|
+
DATA_EXCEPTION = '22000',
|
36
|
+
ARRAY_ELEMENT_ERROR = '2202E',
|
37
|
+
ARRAY_SUBSCRIPT_ERROR = '2202E',
|
38
|
+
CHARACTER_NOT_IN_REPERTOIRE = '22021',
|
39
|
+
DATETIME_FIELD_OVERFLOW = '22008',
|
40
|
+
DATETIME_VALUE_OUT_OF_RANGE = '22008',
|
41
|
+
DIVISION_BY_ZERO = '22012',
|
42
|
+
ERROR_IN_ASSIGNMENT = '22005',
|
43
|
+
ESCAPE_CHARACTER_CONFLICT = '2200B',
|
44
|
+
INDICATOR_OVERFLOW = '22022',
|
45
|
+
INTERVAL_FIELD_OVERFLOW = '22015',
|
46
|
+
INVALID_ARGUMENT_FOR_LOG = '2201E',
|
47
|
+
INVALID_ARGUMENT_FOR_NTILE = '22014',
|
48
|
+
INVALID_ARGUMENT_FOR_NTH_VALUE = '22016',
|
49
|
+
INVALID_ARGUMENT_FOR_POWER_FUNCTION = '2201F',
|
50
|
+
INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION = '2201G',
|
51
|
+
INVALID_CHARACTER_VALUE_FOR_CAST = '22018',
|
52
|
+
INVALID_DATETIME_FORMAT = '22007',
|
53
|
+
INVALID_ESCAPE_CHARACTER = '22019',
|
54
|
+
INVALID_ESCAPE_OCTET = '2200D',
|
55
|
+
INVALID_ESCAPE_SEQUENCE = '22025',
|
56
|
+
NONSTANDARD_USE_OF_ESCAPE_CHARACTER = '22P06',
|
57
|
+
INVALID_INDICATOR_PARAMETER_VALUE = '22010',
|
58
|
+
INVALID_PARAMETER_VALUE = '22023',
|
59
|
+
INVALID_PRECEDING_OR_FOLLOWING_SIZE = '22013',
|
60
|
+
INVALID_REGULAR_EXPRESSION = '2201B',
|
61
|
+
INVALID_ROW_COUNT_IN_LIMIT_CLAUSE = '2201W',
|
62
|
+
INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE = '2201X',
|
63
|
+
INVALID_TABLESAMPLE_ARGUMENT = '2202H',
|
64
|
+
INVALID_TABLESAMPLE_REPEAT = '2202G',
|
65
|
+
INVALID_TIME_ZONE_DISPLACEMENT_VALUE = '22009',
|
66
|
+
INVALID_USE_OF_ESCAPE_CHARACTER = '2200C',
|
67
|
+
MOST_SPECIFIC_TYPE_MISMATCH = '2200G',
|
68
|
+
NULL_VALUE_NOT_ALLOWED = '22004',
|
69
|
+
NULL_VALUE_NO_INDICATOR_PARAMETER = '22002',
|
70
|
+
NUMERIC_VALUE_OUT_OF_RANGE = '22003',
|
71
|
+
SEQUENCE_GENERATOR_LIMIT_EXCEEDED = '2200H',
|
72
|
+
STRING_DATA_LENGTH_MISMATCH = '22026',
|
73
|
+
STRING_DATA_RIGHT_TRUNCATION = '22001',
|
74
|
+
SUBSTRING_ERROR = '22011',
|
75
|
+
TRIM_ERROR = '22027',
|
76
|
+
UNTERMINATED_C_STRING = '22024',
|
77
|
+
ZERO_LENGTH_CHARACTER_STRING = '2200F',
|
78
|
+
FLOATING_POINT_EXCEPTION = '22P01',
|
79
|
+
INVALID_TEXT_REPRESENTATION = '22P02',
|
80
|
+
INVALID_BINARY_REPRESENTATION = '22P03',
|
81
|
+
BAD_COPY_FILE_FORMAT = '22P04',
|
82
|
+
UNTRANSLATABLE_CHARACTER = '22P05',
|
83
|
+
NOT_AN_XML_DOCUMENT = '2200L',
|
84
|
+
INVALID_XML_DOCUMENT = '2200M',
|
85
|
+
INVALID_XML_CONTENT = '2200N',
|
86
|
+
INVALID_XML_COMMENT = '2200S',
|
87
|
+
INVALID_XML_PROCESSING_INSTRUCTION = '2200T',
|
88
|
+
DUPLICATE_JSON_OBJECT_KEY_VALUE = '22030',
|
89
|
+
INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION = '22031',
|
90
|
+
INVALID_JSON_TEXT = '22032',
|
91
|
+
INVALID_SQL_JSON_SUBSCRIPT = '22033',
|
92
|
+
MORE_THAN_ONE_SQL_JSON_ITEM = '22034',
|
93
|
+
NO_SQL_JSON_ITEM = '22035',
|
94
|
+
NON_NUMERIC_SQL_JSON_ITEM = '22036',
|
95
|
+
NON_UNIQUE_KEYS_IN_A_JSON_OBJECT = '22037',
|
96
|
+
SINGLETON_SQL_JSON_ITEM_REQUIRED = '22038',
|
97
|
+
SQL_JSON_ARRAY_NOT_FOUND = '22039',
|
98
|
+
SQL_JSON_MEMBER_NOT_FOUND = '2203A',
|
99
|
+
SQL_JSON_NUMBER_NOT_FOUND = '2203B',
|
100
|
+
SQL_JSON_OBJECT_NOT_FOUND = '2203C',
|
101
|
+
TOO_MANY_JSON_ARRAY_ELEMENTS = '2203D',
|
102
|
+
TOO_MANY_JSON_OBJECT_MEMBERS = '2203E',
|
103
|
+
SQL_JSON_SCALAR_REQUIRED = '2203F',
|
104
|
+
SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE = '2203G',
|
105
|
+
INTEGRITY_CONSTRAINT_VIOLATION = '23000',
|
106
|
+
RESTRICT_VIOLATION = '23001',
|
107
|
+
NOT_NULL_VIOLATION = '23502',
|
108
|
+
FOREIGN_KEY_VIOLATION = '23503',
|
109
|
+
UNIQUE_VIOLATION = '23505',
|
110
|
+
CHECK_VIOLATION = '23514',
|
111
|
+
EXCLUSION_VIOLATION = '23P01',
|
112
|
+
INVALID_CURSOR_STATE = '24000',
|
113
|
+
INVALID_TRANSACTION_STATE = '25000',
|
114
|
+
ACTIVE_SQL_TRANSACTION = '25001',
|
115
|
+
BRANCH_TRANSACTION_ALREADY_ACTIVE = '25002',
|
116
|
+
HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL = '25008',
|
117
|
+
INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION = '25003',
|
118
|
+
INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION = '25004',
|
119
|
+
NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION = '25005',
|
120
|
+
READ_ONLY_SQL_TRANSACTION = '25006',
|
121
|
+
SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED = '25007',
|
122
|
+
NO_ACTIVE_SQL_TRANSACTION = '25P01',
|
123
|
+
IN_FAILED_SQL_TRANSACTION = '25P02',
|
124
|
+
IDLE_IN_TRANSACTION_SESSION_TIMEOUT = '25P03',
|
125
|
+
INVALID_SQL_STATEMENT_NAME = '26000',
|
126
|
+
TRIGGERED_DATA_CHANGE_VIOLATION = '27000',
|
127
|
+
INVALID_AUTHORIZATION_SPECIFICATION = '28000',
|
128
|
+
INVALID_PASSWORD = '28P01',
|
129
|
+
DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST = '2B000',
|
130
|
+
DEPENDENT_OBJECTS_STILL_EXIST = '2BP01',
|
131
|
+
INVALID_TRANSACTION_TERMINATION = '2D000',
|
132
|
+
SQL_ROUTINE_EXCEPTION = '2F000',
|
133
|
+
S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT = '2F005',
|
134
|
+
S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED = '2F002',
|
135
|
+
S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED = '2F003',
|
136
|
+
S_R_E_READING_SQL_DATA_NOT_PERMITTED = '2F004',
|
137
|
+
INVALID_CURSOR_NAME = '34000',
|
138
|
+
EXTERNAL_ROUTINE_EXCEPTION = '38000',
|
139
|
+
E_R_E_CONTAINING_SQL_NOT_PERMITTED = '38001',
|
140
|
+
E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED = '38002',
|
141
|
+
E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED = '38003',
|
142
|
+
E_R_E_READING_SQL_DATA_NOT_PERMITTED = '38004',
|
143
|
+
EXTERNAL_ROUTINE_INVOCATION_EXCEPTION = '39000',
|
144
|
+
E_R_I_E_INVALID_SQLSTATE_RETURNED = '39001',
|
145
|
+
E_R_I_E_NULL_VALUE_NOT_ALLOWED = '39004',
|
146
|
+
E_R_I_E_TRIGGER_PROTOCOL_VIOLATED = '39P01',
|
147
|
+
E_R_I_E_SRF_PROTOCOL_VIOLATED = '39P02',
|
148
|
+
E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED = '39P03',
|
149
|
+
SAVEPOINT_EXCEPTION = '3B000',
|
150
|
+
S_E_INVALID_SPECIFICATION = '3B001',
|
151
|
+
INVALID_CATALOG_NAME = '3D000',
|
152
|
+
INVALID_SCHEMA_NAME = '3F000',
|
153
|
+
TRANSACTION_ROLLBACK = '40000',
|
154
|
+
T_R_INTEGRITY_CONSTRAINT_VIOLATION = '40002',
|
155
|
+
T_R_SERIALIZATION_FAILURE = '40001',
|
156
|
+
T_R_STATEMENT_COMPLETION_UNKNOWN = '40003',
|
157
|
+
T_R_DEADLOCK_DETECTED = '40P01',
|
158
|
+
SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION = '42000',
|
159
|
+
SYNTAX_ERROR = '42601',
|
160
|
+
INSUFFICIENT_PRIVILEGE = '42501',
|
161
|
+
CANNOT_COERCE = '42846',
|
162
|
+
GROUPING_ERROR = '42803',
|
163
|
+
WINDOWING_ERROR = '42P20',
|
164
|
+
INVALID_RECURSION = '42P19',
|
165
|
+
INVALID_FOREIGN_KEY = '42830',
|
166
|
+
INVALID_NAME = '42602',
|
167
|
+
NAME_TOO_LONG = '42622',
|
168
|
+
RESERVED_NAME = '42939',
|
169
|
+
DATATYPE_MISMATCH = '42804',
|
170
|
+
INDETERMINATE_DATATYPE = '42P18',
|
171
|
+
COLLATION_MISMATCH = '42P21',
|
172
|
+
INDETERMINATE_COLLATION = '42P22',
|
173
|
+
WRONG_OBJECT_TYPE = '42809',
|
174
|
+
GENERATED_ALWAYS = '428C9',
|
175
|
+
UNDEFINED_COLUMN = '42703',
|
176
|
+
UNDEFINED_CURSOR = '34000',
|
177
|
+
UNDEFINED_DATABASE = '3D000',
|
178
|
+
UNDEFINED_FUNCTION = '42883',
|
179
|
+
UNDEFINED_PSTATEMENT = '26000',
|
180
|
+
UNDEFINED_SCHEMA = '3F000',
|
181
|
+
UNDEFINED_TABLE = '42P01',
|
182
|
+
UNDEFINED_PARAMETER = '42P02',
|
183
|
+
UNDEFINED_OBJECT = '42704',
|
184
|
+
DUPLICATE_COLUMN = '42701',
|
185
|
+
DUPLICATE_CURSOR = '42P03',
|
186
|
+
DUPLICATE_DATABASE = '42P04',
|
187
|
+
DUPLICATE_FUNCTION = '42723',
|
188
|
+
DUPLICATE_PSTATEMENT = '42P05',
|
189
|
+
DUPLICATE_SCHEMA = '42P06',
|
190
|
+
DUPLICATE_TABLE = '42P07',
|
191
|
+
DUPLICATE_ALIAS = '42712',
|
192
|
+
DUPLICATE_OBJECT = '42710',
|
193
|
+
AMBIGUOUS_COLUMN = '42702',
|
194
|
+
AMBIGUOUS_FUNCTION = '42725',
|
195
|
+
AMBIGUOUS_PARAMETER = '42P08',
|
196
|
+
AMBIGUOUS_ALIAS = '42P09',
|
197
|
+
INVALID_COLUMN_REFERENCE = '42P10',
|
198
|
+
INVALID_COLUMN_DEFINITION = '42611',
|
199
|
+
INVALID_CURSOR_DEFINITION = '42P11',
|
200
|
+
INVALID_DATABASE_DEFINITION = '42P12',
|
201
|
+
INVALID_FUNCTION_DEFINITION = '42P13',
|
202
|
+
INVALID_PSTATEMENT_DEFINITION = '42P14',
|
203
|
+
INVALID_SCHEMA_DEFINITION = '42P15',
|
204
|
+
INVALID_TABLE_DEFINITION = '42P16',
|
205
|
+
INVALID_OBJECT_DEFINITION = '42P17',
|
206
|
+
WITH_CHECK_OPTION_VIOLATION = '44000',
|
207
|
+
INSUFFICIENT_RESOURCES = '53000',
|
208
|
+
DISK_FULL = '53100',
|
209
|
+
OUT_OF_MEMORY = '53200',
|
210
|
+
TOO_MANY_CONNECTIONS = '53300',
|
211
|
+
CONFIGURATION_LIMIT_EXCEEDED = '53400',
|
212
|
+
PROGRAM_LIMIT_EXCEEDED = '54000',
|
213
|
+
STATEMENT_TOO_COMPLEX = '54001',
|
214
|
+
TOO_MANY_COLUMNS = '54011',
|
215
|
+
TOO_MANY_ARGUMENTS = '54023',
|
216
|
+
OBJECT_NOT_IN_PREREQUISITE_STATE = '55000',
|
217
|
+
OBJECT_IN_USE = '55006',
|
218
|
+
CANT_CHANGE_RUNTIME_PARAM = '55P02',
|
219
|
+
LOCK_NOT_AVAILABLE = '55P03',
|
220
|
+
UNSAFE_NEW_ENUM_VALUE_USAGE = '55P04',
|
221
|
+
OPERATOR_INTERVENTION = '57000',
|
222
|
+
QUERY_CANCELED = '57014',
|
223
|
+
ADMIN_SHUTDOWN = '57P01',
|
224
|
+
CRASH_SHUTDOWN = '57P02',
|
225
|
+
CANNOT_CONNECT_NOW = '57P03',
|
226
|
+
DATABASE_DROPPED = '57P04',
|
227
|
+
IDLE_SESSION_TIMEOUT = '57P05',
|
228
|
+
SYSTEM_ERROR = '58000',
|
229
|
+
IO_ERROR = '58030',
|
230
|
+
UNDEFINED_FILE = '58P01',
|
231
|
+
DUPLICATE_FILE = '58P02',
|
232
|
+
SNAPSHOT_TOO_OLD = '72000',
|
233
|
+
CONFIG_FILE_ERROR = 'F0000',
|
234
|
+
LOCK_FILE_EXISTS = 'F0001',
|
235
|
+
FDW_ERROR = 'HV000',
|
236
|
+
FDW_COLUMN_NAME_NOT_FOUND = 'HV005',
|
237
|
+
FDW_DYNAMIC_PARAMETER_VALUE_NEEDED = 'HV002',
|
238
|
+
FDW_FUNCTION_SEQUENCE_ERROR = 'HV010',
|
239
|
+
FDW_INCONSISTENT_DESCRIPTOR_INFORMATION = 'HV021',
|
240
|
+
FDW_INVALID_ATTRIBUTE_VALUE = 'HV024',
|
241
|
+
FDW_INVALID_COLUMN_NAME = 'HV007',
|
242
|
+
FDW_INVALID_COLUMN_NUMBER = 'HV008',
|
243
|
+
FDW_INVALID_DATA_TYPE = 'HV004',
|
244
|
+
FDW_INVALID_DATA_TYPE_DESCRIPTORS = 'HV006',
|
245
|
+
FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER = 'HV091',
|
246
|
+
FDW_INVALID_HANDLE = 'HV00B',
|
247
|
+
FDW_INVALID_OPTION_INDEX = 'HV00C',
|
248
|
+
FDW_INVALID_OPTION_NAME = 'HV00D',
|
249
|
+
FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH = 'HV090',
|
250
|
+
FDW_INVALID_STRING_FORMAT = 'HV00A',
|
251
|
+
FDW_INVALID_USE_OF_NULL_POINTER = 'HV009',
|
252
|
+
FDW_TOO_MANY_HANDLES = 'HV014',
|
253
|
+
FDW_OUT_OF_MEMORY = 'HV001',
|
254
|
+
FDW_NO_SCHEMAS = 'HV00P',
|
255
|
+
FDW_OPTION_NAME_NOT_FOUND = 'HV00J',
|
256
|
+
FDW_REPLY_HANDLE = 'HV00K',
|
257
|
+
FDW_SCHEMA_NOT_FOUND = 'HV00Q',
|
258
|
+
FDW_TABLE_NOT_FOUND = 'HV00R',
|
259
|
+
FDW_UNABLE_TO_CREATE_EXECUTION = 'HV00L',
|
260
|
+
FDW_UNABLE_TO_CREATE_REPLY = 'HV00M',
|
261
|
+
FDW_UNABLE_TO_ESTABLISH_CONNECTION = 'HV00N',
|
262
|
+
PLPGSQL_ERROR = 'P0000',
|
263
|
+
RAISE_EXCEPTION = 'P0001',
|
264
|
+
NO_DATA_FOUND = 'P0002',
|
265
|
+
TOO_MANY_ROWS = 'P0003',
|
266
|
+
ASSERT_FAILURE = 'P0004',
|
267
|
+
INTERNAL_ERROR = 'XX000',
|
268
|
+
DATA_CORRUPTED = 'XX001',
|
269
|
+
INDEX_CORRUPTED = 'XX002',
|
270
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { assertAssign } from './assertAssign';
|
2
|
+
import { assertIsString } from './assertIsString';
|
3
|
+
|
4
|
+
describe('assertAssign', () => {
|
5
|
+
it('should assert the input meets an assertion function and return if valid', () => {
|
6
|
+
expect(assertAssign('123', assertIsString)).toEqual('123');
|
7
|
+
});
|
8
|
+
|
9
|
+
it('should assert the input meets an assertion function and throw if not valid', () => {
|
10
|
+
expect(() => assertAssign(123, assertIsString)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
|
11
|
+
});
|
12
|
+
|
13
|
+
it('should allow custom error message', () => {
|
14
|
+
expect(() => assertAssign(123, assertIsString, 'custom error')).toThrowErrorMatchingInlineSnapshot(
|
15
|
+
`"custom error"`,
|
16
|
+
);
|
17
|
+
});
|
18
|
+
});
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { AssertionError } from 'assert';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Assert and return unknown value as known type
|
5
|
+
*/
|
6
|
+
export function assertAssign<T>(
|
7
|
+
value: unknown,
|
8
|
+
assertionFunc: (val: unknown) => asserts val is T,
|
9
|
+
errorMessage?: string,
|
10
|
+
): T {
|
11
|
+
try {
|
12
|
+
assertionFunc(value);
|
13
|
+
} catch (e) {
|
14
|
+
if (errorMessage) {
|
15
|
+
throw new AssertionError({ message: errorMessage });
|
16
|
+
} else {
|
17
|
+
throw e;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
return value;
|
22
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { assertAssignWithDefaultUndefinedValue } from './assertAssignWithDefaultUndefinedValue';
|
2
|
+
import { assertIsObject } from './assertIsObject';
|
3
|
+
import { assertIsString } from './assertIsString';
|
4
|
+
|
5
|
+
describe('assertAssignWithDefaultUndefinedValue', () => {
|
6
|
+
it('should assert the input meets an assertion function and return if valid', () => {
|
7
|
+
expect(assertAssignWithDefaultUndefinedValue('123', assertIsString, 'xx')).toEqual('123');
|
8
|
+
});
|
9
|
+
|
10
|
+
it('should return default value if input is undefined', () => {
|
11
|
+
expect(assertAssignWithDefaultUndefinedValue(undefined, assertIsString, 'xx')).toEqual('xx');
|
12
|
+
});
|
13
|
+
|
14
|
+
it('should handle different types of type assertions', () => {
|
15
|
+
expect(assertAssignWithDefaultUndefinedValue(undefined, assertIsObject, { default: 'hello' })).toEqual({
|
16
|
+
default: 'hello',
|
17
|
+
});
|
18
|
+
});
|
19
|
+
|
20
|
+
it('should assert the input meets an assertion function and throw if not valid', () => {
|
21
|
+
expect(() => assertAssignWithDefaultUndefinedValue(123, assertIsString, 'xx')).toThrowErrorMatchingInlineSnapshot(
|
22
|
+
`"value is not a string"`,
|
23
|
+
);
|
24
|
+
});
|
25
|
+
|
26
|
+
it('should allow custom error message', () => {
|
27
|
+
expect(() =>
|
28
|
+
assertAssignWithDefaultUndefinedValue(123, assertIsString, 'xx', 'custom error'),
|
29
|
+
).toThrowErrorMatchingInlineSnapshot(`"custom error"`);
|
30
|
+
});
|
31
|
+
});
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { assertAssign } from './assertAssign';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* If value is undefined, return default value otherwise assert it meets assertion rule
|
5
|
+
*/
|
6
|
+
export function assertAssignWithDefaultUndefinedValue<T>(
|
7
|
+
value: unknown,
|
8
|
+
assertionFunc: (val: unknown) => asserts val is T,
|
9
|
+
defaultValue: T,
|
10
|
+
errorMessage?: string,
|
11
|
+
): T {
|
12
|
+
if (value === undefined || value === null) {
|
13
|
+
return defaultValue;
|
14
|
+
}
|
15
|
+
|
16
|
+
return assertAssign(value, assertionFunc, errorMessage);
|
17
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { assertIsDefined } from './assertIsDefined';
|
2
|
+
|
3
|
+
describe('assertIsDefined', () => {
|
4
|
+
it('should throw an assertion error if input is undefined', () => {
|
5
|
+
expect(() => assertIsDefined(undefined)).toThrowErrorMatchingInlineSnapshot(
|
6
|
+
`"Expected 'val' to be defined, but received undefined"`,
|
7
|
+
);
|
8
|
+
});
|
9
|
+
|
10
|
+
it('should throw an assertion error if input is null', () => {
|
11
|
+
expect(() => assertIsDefined(null)).toThrowErrorMatchingInlineSnapshot(
|
12
|
+
`"Expected 'val' to be defined, but received null"`,
|
13
|
+
);
|
14
|
+
});
|
15
|
+
|
16
|
+
it('should return "void" or undefined if valid', () => {
|
17
|
+
expect(assertIsDefined(123)).toBeUndefined();
|
18
|
+
});
|
19
|
+
});
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { AssertionError } from 'assert';
|
2
|
+
|
3
|
+
export function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
|
4
|
+
if (value === undefined || value === null) {
|
5
|
+
throw new AssertionError({ message: `Expected 'val' to be defined, but received ${value}` });
|
6
|
+
}
|
7
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { assertIsMapOfStringString } from './assertIsMapOfStringString';
|
2
|
+
|
3
|
+
describe('assertIsMapOfStringString', () => {
|
4
|
+
it('should not throw if input is an object with only string values', () => {
|
5
|
+
expect(assertIsMapOfStringString({ prop: 'aa' })).toBeUndefined();
|
6
|
+
});
|
7
|
+
it('should not throw if input is an empty object', () => {
|
8
|
+
expect(assertIsMapOfStringString({})).toBeUndefined();
|
9
|
+
});
|
10
|
+
|
11
|
+
it("should throw if object property isn't a string", () => {
|
12
|
+
expect(() => assertIsMapOfStringString({ prop: 123 })).toThrowErrorMatchingInlineSnapshot(
|
13
|
+
`"property prop is not type of string"`,
|
14
|
+
);
|
15
|
+
});
|
16
|
+
|
17
|
+
it('should throw if input is not an object', () => {
|
18
|
+
expect(() => assertIsMapOfStringString(123)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
|
19
|
+
expect(() => assertIsMapOfStringString(undefined)).toThrowErrorMatchingInlineSnapshot(
|
20
|
+
`"value cannot be null or undefined"`,
|
21
|
+
);
|
22
|
+
});
|
23
|
+
});
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { AssertionError } from 'assert';
|
2
|
+
|
3
|
+
export function assertIsMapOfStringString(value: unknown): asserts value is { [key: string]: string } {
|
4
|
+
if (!value) {
|
5
|
+
throw new AssertionError({
|
6
|
+
actual: value,
|
7
|
+
expected: 'not null',
|
8
|
+
message: 'value cannot be null or undefined',
|
9
|
+
});
|
10
|
+
}
|
11
|
+
|
12
|
+
if (typeof value !== 'object') {
|
13
|
+
throw new AssertionError({
|
14
|
+
actual: value,
|
15
|
+
expected: 'string',
|
16
|
+
message: 'value is not a string',
|
17
|
+
});
|
18
|
+
}
|
19
|
+
|
20
|
+
for (const [key, propValue] of Object.entries(value)) {
|
21
|
+
if (typeof propValue !== 'string') {
|
22
|
+
throw new AssertionError({
|
23
|
+
actual: typeof propValue,
|
24
|
+
expected: 'string',
|
25
|
+
message: `property ${key} is not type of string`,
|
26
|
+
});
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { assertIsNotAnEmptyString } from './assertIsNotAnEmptyString';
|
2
|
+
|
3
|
+
describe('assertIsNotAnEmptyString', () => {
|
4
|
+
it('should throw if input value is not a string', () => {
|
5
|
+
expect(() => assertIsNotAnEmptyString(null)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
|
6
|
+
expect(() => assertIsNotAnEmptyString(undefined)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
|
7
|
+
expect(() => assertIsNotAnEmptyString(132)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
|
8
|
+
expect(() => assertIsNotAnEmptyString({})).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
|
9
|
+
expect(() => assertIsNotAnEmptyString(['aa'])).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
|
10
|
+
});
|
11
|
+
|
12
|
+
it('should throw if input is empty string', () => {
|
13
|
+
expect(() => assertIsNotAnEmptyString('')).toThrowErrorMatchingInlineSnapshot(`"string cannot be empty"`);
|
14
|
+
});
|
15
|
+
|
16
|
+
it('should throw if input is whitespace string', () => {
|
17
|
+
expect(() => assertIsNotAnEmptyString(' ')).toThrowErrorMatchingInlineSnapshot(`"string cannot be empty"`);
|
18
|
+
expect(() => assertIsNotAnEmptyString(' ')).toThrowErrorMatchingInlineSnapshot(`"string cannot be empty"`);
|
19
|
+
});
|
20
|
+
|
21
|
+
it('should not throw if string of length >= 1 is passed in', () => {
|
22
|
+
expect(assertIsNotAnEmptyString('1')).toBeUndefined();
|
23
|
+
expect(assertIsNotAnEmptyString('11111')).toBeUndefined();
|
24
|
+
});
|
25
|
+
});
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { AssertionError } from 'assert';
|
2
|
+
|
3
|
+
export function assertIsNotAnEmptyString(val: any): asserts val is string {
|
4
|
+
if (typeof val !== 'string') {
|
5
|
+
throw new AssertionError({
|
6
|
+
actual: typeof val,
|
7
|
+
expected: 'string',
|
8
|
+
message: 'value is not a string',
|
9
|
+
});
|
10
|
+
}
|
11
|
+
|
12
|
+
if (val.trim().length === 0) {
|
13
|
+
throw new AssertionError({
|
14
|
+
message: 'string cannot be empty',
|
15
|
+
});
|
16
|
+
}
|
17
|
+
}
|