• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ngageoint / geopackage-js / 4078143969

pending completion
4078143969

push

github

Christopher Caldwell
bump version

3593 of 8015 branches covered (44.83%)

Branch coverage included in aggregate %.

15102 of 20471 relevant lines covered (73.77%)

1564.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.27
/lib/db/table/constraintParser.ts
1
/**
2
 * SQL constraint parser from create table statements
3
 */
4
import { TableConstraints } from './tableConstraints';
1✔
5
import { ColumnConstraints } from './columnConstraints';
1✔
6
import { Constraint } from './constraint';
1✔
7
import { ConstraintType } from './constraintType';
1✔
8
import { RawConstraint } from './rawConstraint';
1✔
9
import { StringUtils } from '../stringUtils';
1✔
10

11
export class ConstraintParser {
1✔
12
  /**
13
   * Constraint name pattern
14
   */
15
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
16
  static NAME_PATTERN = (s: string) => s.match(/CONSTRAINT\s+("[\s\S]+"|\S+)\s/i);
45,419✔
17

18
  /**
19
   * Constraint name pattern name matcher group
20
   */
21
  static NAME_PATTERN_NAME_GROUP = 1;
1✔
22

23
  /**
24
   * Constraint name and definition pattern
25
   */
26
  static CONSTRAINT_PATTERN = (s: string): string[] => s.match(/(CONSTRAINT\s+("[\s\S]+"|\S+)\s)?([\s\S]*)/i);
26,622✔
27

28
  /**
29
   * Constraint name and definition pattern name matcher group
30
   */
31
  static CONSTRAINT_PATTERN_NAME_GROUP = 2;
1✔
32

33
  /**
34
   * Constraint name and definition pattern definition matcher group
35
   */
36
  static CONSTRAINT_PATTERN_DEFINITION_GROUP = 3;
1✔
37

38
  /**
39
   * Get the constraints for the table SQL
40
   * @param tableSql table SQL
41
   * @return constraints
42
   */
43
  static getConstraints(tableSql: string): TableConstraints {
1✔
44
    const constraints = new TableConstraints();
5,221✔
45
    // Find the start and end of the column definitions and table
46
    // constraints
47
    let start = -1;
5,221✔
48
    let end = -1;
5,221✔
49

50
    if (tableSql !== null && tableSql !== undefined) {
5,221!
51
      start = tableSql.indexOf('(');
5,221✔
52
      end = tableSql.lastIndexOf(')');
5,221✔
53
    }
54

55
    if (start >= 0 && end >= 0) {
5,221✔
56
      const definitions = tableSql.substring(start + 1, end).trim();
5,218✔
57

58
      // Parse the column definitions and table constraints, divided by
59
      // columns when not within parentheses. Create constraints when
60
      // found.
61
      let openParentheses = 0;
5,218✔
62
      let sqlStart = 0;
5,218✔
63

64
      for (let i = 0; i < definitions.length; i++) {
5,218✔
65
        const character = definitions.charAt(i);
749,241✔
66
        if (character === '(') {
749,241✔
67
          openParentheses++;
3,791✔
68
        } else if (character === ')') {
745,450✔
69
          openParentheses--;
3,791✔
70
        } else if (character === ',' && openParentheses === 0) {
741,659✔
71
          const constraintSql = definitions.substring(sqlStart, i);
20,374✔
72
          ConstraintParser.addConstraints(constraints, constraintSql);
20,374✔
73
          sqlStart = i + 1;
20,374✔
74
        }
75
      }
76
      if (sqlStart < definitions.length) {
5,218!
77
        const constraintSql = definitions.substring(sqlStart, definitions.length);
5,218✔
78
        ConstraintParser.addConstraints(constraints, constraintSql);
5,218✔
79
      }
80
    }
81

82
    return constraints;
5,221✔
83
  }
84

85
  /**
86
   * Add constraints of the optional type or all constraints
87
   * @param constraints constraints to add to
88
   * @param constraintSql constraint SQL statement
89
   */
90
  static addConstraints(constraints: TableConstraints, constraintSql: string): void {
1✔
91
    const constraint = ConstraintParser.getTableConstraint(constraintSql);
25,592✔
92
    if (constraint !== null && constraint !== undefined) {
25,592✔
93
      constraints.addTableConstraint(constraint);
3,614✔
94
    } else {
95
      const columnConstraints = ConstraintParser.getColumnConstraints(constraintSql);
21,978✔
96
      if (columnConstraints.hasConstraints()) {
21,978✔
97
        constraints.addColumnConstraints(columnConstraints);
13,720✔
98
      }
99
    }
100
  }
101

102
  /**
103
   * Attempt to get column constraints by parsing the SQL statement
104
   * @param constraintSql constraint SQL statement
105
   * @return constraints
106
   */
107
  static getColumnConstraints(constraintSql: string): ColumnConstraints {
1✔
108
    const parts = constraintSql.trim().split(/\s+/);
21,978✔
109
    const columnName = StringUtils.quoteUnwrap(parts[0]);
21,978✔
110

111
    const constraints = new ColumnConstraints(columnName);
21,978✔
112

113
    let constraintIndex = -1;
21,978✔
114
    let constraintType = null;
21,978✔
115

116
    for (let i = 1; i < parts.length; i++) {
21,978✔
117
      const part = parts[i];
60,942✔
118
      if (Constraint.CONSTRAINT === part.toUpperCase()) {
60,942✔
119
        if (constraintType !== null && constraintType !== undefined) {
1!
120
          constraints.addConstraint(ConstraintParser.createConstraint(parts, constraintIndex, i, constraintType));
×
121
          constraintType = null;
×
122
        }
123
        constraintIndex = i;
1✔
124
      } else {
125
        const type = ConstraintType.getColumnType(part);
60,941✔
126
        if (type !== null && type !== undefined) {
60,941✔
127
          if (constraintType !== null && constraintType !== undefined) {
21,022✔
128
            constraints.addConstraint(ConstraintParser.createConstraint(parts, constraintIndex, i, constraintType));
7,302✔
129
            constraintIndex = -1;
7,302✔
130
          }
131
          if (constraintIndex < 0) {
21,022✔
132
            constraintIndex = i;
21,021✔
133
          }
134
          constraintType = type;
21,022✔
135
        }
136
      }
137
    }
138
    if (constraintType !== null && constraintType !== undefined) {
21,978✔
139
      constraints.addConstraint(
13,720✔
140
        ConstraintParser.createConstraint(parts, constraintIndex, parts.length, constraintType),
141
      );
142
    }
143
    return constraints;
21,978✔
144
  }
145

146
  /**
147
   * Create a constraint from the SQL parts with the range for the type
148
   * @param parts SQL parts
149
   * @param startIndex start index (inclusive)
150
   * @param endIndex end index (exclusive)
151
   * @param type constraint type
152
   * @return constraint
153
   */
154
  static createConstraint(parts: string[], startIndex: number, endIndex: number, type: ConstraintType): Constraint {
1✔
155
    let constraintSql = '';
21,022✔
156
    for (let i = startIndex; i < endIndex; i++) {
21,022✔
157
      if (constraintSql.length > 0) {
38,476✔
158
        constraintSql = constraintSql.concat(' ');
17,454✔
159
      }
160
      constraintSql = constraintSql.concat(parts[i]);
38,476✔
161
    }
162
    const name = ConstraintParser.getName(constraintSql);
21,022✔
163
    return new RawConstraint(type, name, constraintSql);
21,022✔
164
  }
165

166
  /**
167
   * Attempt to get the constraint by parsing the SQL statement
168
   * @param constraintSql constraint SQL statement
169
   * @param table true to search for a table constraint, false to search for a column constraint
170
   * @return constraint or null
171
   */
172
  static getConstraint(constraintSql: string, table: boolean): Constraint {
1✔
173
    let constraint = null;
26,622✔
174
    const nameAndDefinition = ConstraintParser.getNameAndDefinition(constraintSql);
26,622✔
175
    const definition = nameAndDefinition[1];
26,622✔
176
    if (definition !== null && definition !== undefined) {
26,622!
177
      const prefix = definition.split(/\s+/)[0];
26,622✔
178
      let type;
26,622✔
179
      if (table) {
26,622✔
180
        type = ConstraintType.getTableType(prefix);
26,187✔
181
      } else {
182
        type = ConstraintType.getColumnType(prefix);
435✔
183
      }
184
      if (type !== null && type !== undefined) {
26,622✔
185
        constraint = new RawConstraint(type, nameAndDefinition[0], constraintSql.trim());
4,209✔
186
      }
187
    }
188
    return constraint;
26,622✔
189
  }
190

191
  /**
192
   * Attempt to get a table constraint by parsing the SQL statement
193
   * @param constraintSql constraint SQL statement
194
   * @return constraint or null
195
   */
196
  static getTableConstraint(constraintSql: string): Constraint {
1✔
197
    return ConstraintParser.getConstraint(constraintSql, true);
26,187✔
198
  }
199

200
  /**
201
   * Check if the SQL is a table type constraint
202
   * @param constraintSql constraint SQL statement
203
   * @return true if a table constraint
204
   */
205
  static isTableConstraint(constraintSql: string): boolean {
1✔
206
    return ConstraintParser.getTableConstraint(constraintSql) !== null;
×
207
  }
208

209
  /**
210
   * Get the table constraint type of the constraint SQL
211
   * @param constraintSql constraint SQL
212
   * @return constraint type or null
213
   */
214
  static getTableType(constraintSql: string): ConstraintType {
1✔
215
    let type = null;
×
216
    const constraint = ConstraintParser.getTableConstraint(constraintSql);
×
217
    if (constraint != null) {
×
218
      type = constraint.type;
×
219
    }
220
    return type;
×
221
  }
222

223
  /**
224
   * Determine if the table constraint SQL is the constraint type
225
   * @param type constraint type
226
   * @param constraintSql constraint SQL
227
   * @return true if the constraint type
228
   */
229
  static isTableType(type: ConstraintType, constraintSql: string): boolean {
1✔
230
    let isType = false;
×
231
    const constraintType = ConstraintParser.getTableType(constraintSql);
×
232
    if (constraintType != null) {
×
233
      isType = type === constraintType;
×
234
    }
235
    return isType;
×
236
  }
237

238
  /**
239
   * Attempt to get a column constraint by parsing the SQL statement
240
   * @param constraintSql constraint SQL statement
241
   * @return constraint or null
242
   */
243
  static getColumnConstraint(constraintSql: string): Constraint {
1✔
244
    return ConstraintParser.getConstraint(constraintSql, false);
435✔
245
  }
246

247
  /**
248
   * Check if the SQL is a column type constraint
249
   * @param constraintSql constraint SQL statement
250
   * @return true if a column constraint
251
   */
252
  static isColumnConstraint(constraintSql: string): boolean {
1✔
253
    return ConstraintParser.getColumnConstraint(constraintSql) != null;
×
254
  }
255

256
  /**
257
   * Get the column constraint type of the constraint SQL
258
   * @param constraintSql constraint SQL
259
   * @return constraint type or null
260
   */
261
  static getColumnType(constraintSql: string): ConstraintType {
1✔
262
    let type = null;
×
263
    const constraint = ConstraintParser.getColumnConstraint(constraintSql);
×
264
    if (constraint != null) {
×
265
      type = constraint.type;
×
266
    }
267
    return type;
×
268
  }
269

270
  /**
271
   * Determine if the column constraint SQL is the constraint type
272
   * @param type constraint type
273
   * @param constraintSql constraint SQL
274
   * @return true if the constraint type
275
   */
276
  static isColumnType(type: ConstraintType, constraintSql: string): boolean {
1✔
277
    let isType = false;
×
278
    const constraintType = ConstraintParser.getColumnType(constraintSql);
×
279
    if (constraintType != null) {
×
280
      isType = type == constraintType;
×
281
    }
282
    return isType;
×
283
  }
284

285
  /**
286
   * Attempt to get a constraint by parsing the SQL statement
287
   * @param constraintSql constraint SQL statement
288
   * @return constraint or null
289
   */
290
  static getTableOrColumnConstraint(constraintSql: string): Constraint {
1✔
291
    let constraint = ConstraintParser.getTableConstraint(constraintSql);
595✔
292
    if (constraint === null || constraint === undefined) {
595✔
293
      constraint = ConstraintParser.getColumnConstraint(constraintSql);
435✔
294
    }
295
    return constraint;
595✔
296
  }
297

298
  /**
299
   * Check if the SQL is a constraint
300
   * @param constraintSql constraint SQL statement
301
   * @return true if a constraint
302
   */
303
  static isConstraint(constraintSql: string): boolean {
1✔
304
    return ConstraintParser.getTableOrColumnConstraint(constraintSql) !== null;
×
305
  }
306

307
  /**
308
   * Get the constraint type of the constraint SQL
309
   * @param constraintSql constraint SQL
310
   * @return constraint type or null
311
   */
312
  static getType(constraintSql: string): ConstraintType {
1✔
313
    let type = null;
476✔
314
    const constraint = ConstraintParser.getTableOrColumnConstraint(constraintSql);
476✔
315
    if (constraint !== null && constraint !== undefined) {
476!
316
      type = constraint.getType();
476✔
317
    }
318
    return type;
476✔
319
  }
320

321
  /**
322
   * Determine if the constraint SQL is the constraint type
323
   * @param type constraint type
324
   * @param constraintSql constraint SQL
325
   * @return true if the constraint type
326
   */
327
  static isType(type: ConstraintType, constraintSql: string): boolean {
1✔
328
    let isType = false;
238✔
329
    const constraintType = ConstraintParser.getType(constraintSql);
238✔
330
    if (constraintType !== null && constraintType !== undefined) {
238!
331
      isType = type === constraintType;
238✔
332
    }
333
    return isType;
238✔
334
  }
335

336
  /**
337
   * Get the constraint name if it has one
338
   * @param constraintSql constraint SQL
339
   * @return constraint name or null
340
   */
341
  static getName(constraintSql: string): string {
1✔
342
    let name = null;
45,419✔
343
    const matches = ConstraintParser.NAME_PATTERN(constraintSql);
45,419✔
344
    if (matches !== null && matches.length > ConstraintParser.NAME_PATTERN_NAME_GROUP) {
45,419✔
345
      name = StringUtils.quoteUnwrap(matches[ConstraintParser.NAME_PATTERN_NAME_GROUP]);
45✔
346
    }
347
    return name;
45,419✔
348
  }
349

350
  /**
351
   * Get the constraint name and remaining definition
352
   * @param constraintSql constraint SQL
353
   * @return array with name or null at index 0, definition at index 1
354
   */
355
  static getNameAndDefinition(constraintSql: string): string[] {
1✔
356
    let parts = [null, constraintSql];
26,622✔
357
    const matches = ConstraintParser.CONSTRAINT_PATTERN(constraintSql.trim());
26,622✔
358
    if (matches !== null && matches.length > ConstraintParser.CONSTRAINT_PATTERN_DEFINITION_GROUP) {
26,622!
359
      let name = StringUtils.quoteUnwrap(matches[ConstraintParser.CONSTRAINT_PATTERN_NAME_GROUP]);
26,622✔
360
      if (name !== null && name !== undefined) {
26,622✔
361
        name = name.trim();
233✔
362
      }
363
      let definition = matches[ConstraintParser.CONSTRAINT_PATTERN_DEFINITION_GROUP];
26,622✔
364
      if (definition !== null && definition !== undefined) {
26,622!
365
        definition = definition.trim();
26,622✔
366
      }
367
      parts = [name, definition];
26,622✔
368
    }
369
    return parts;
26,622✔
370
  }
371
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc