typesql-cli 0.18.5 → 0.19.0
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/cli.js +5 -10
- package/cli.js.map +1 -1
- package/code-generator2.js +3 -3
- package/code-generator2.js.map +1 -1
- package/dialects/postgres.d.ts +1 -1
- package/dialects/postgres.d.ts.map +1 -1
- package/dialects/postgres.js +228 -23
- package/dialects/postgres.js.map +1 -1
- package/load-config.d.ts +4 -0
- package/load-config.d.ts.map +1 -0
- package/load-config.js +25 -0
- package/load-config.js.map +1 -0
- package/package.json +1 -1
- package/postgres-query-analyzer/describe.js +1 -1
- package/postgres-query-analyzer/describe.js.map +1 -1
- package/postgres-query-analyzer/traverse.d.ts.map +1 -1
- package/postgres-query-analyzer/traverse.js +99 -54
- package/postgres-query-analyzer/traverse.js.map +1 -1
- package/sqlite-query-analyzer/types.d.ts +1 -1
- package/sqlite-query-analyzer/types.d.ts.map +1 -1
@@ -124,8 +124,12 @@ function traverse_select_no_parens(select_no_parens, context, traverseResult) {
|
|
124
124
|
if (with_clause) {
|
125
125
|
with_clause.cte_list().common_table_expr_list()
|
126
126
|
.forEach(common_table_expr => {
|
127
|
-
|
128
|
-
|
127
|
+
var _a, _b, _c;
|
128
|
+
const recursiveTableName = with_clause.RECURSIVE() ? common_table_expr.name().getText() : undefined;
|
129
|
+
const recursiveColumnNames = (_c = (_b = (_a = common_table_expr.name_list_()) === null || _a === void 0 ? void 0 : _a.name_list()) === null || _b === void 0 ? void 0 : _b.name_list()) === null || _c === void 0 ? void 0 : _c.map(name => name.getText());
|
130
|
+
const withResult = traverse_common_table_expr(common_table_expr, Object.assign(Object.assign({}, context), { recursiveTableName, recursiveColumnNames }), traverseResult);
|
131
|
+
const columns = recursiveColumnNames ? withResult.map((col, index) => { var _a; return (Object.assign(Object.assign({}, col), { column_name: (_a = recursiveColumnNames[index]) !== null && _a !== void 0 ? _a : col.column_name })); }) : withResult;
|
132
|
+
context.withColumns.push(...columns);
|
129
133
|
});
|
130
134
|
}
|
131
135
|
const select_clause = select_no_parens.select_clause();
|
@@ -182,8 +186,11 @@ function traverse_select_clause(select_clause, context, traverseResult) {
|
|
182
186
|
const mainSelectResult = traverse_simple_select_intersect(simple_select_intersect_list[0], context, traverseResult);
|
183
187
|
let columns = mainSelectResult.columns;
|
184
188
|
//union
|
189
|
+
const { recursiveTableName, recursiveColumnNames } = context;
|
190
|
+
const recursiveColumns = recursiveTableName ? mainSelectResult.columns
|
191
|
+
.map((col, index) => { var _a; return (Object.assign(Object.assign({}, col), { table_name: recursiveTableName, column_name: (_a = recursiveColumnNames === null || recursiveColumnNames === void 0 ? void 0 : recursiveColumnNames[index]) !== null && _a !== void 0 ? _a : col.column_name })); }) : [];
|
185
192
|
for (let index = 1; index < simple_select_intersect_list.length; index++) {
|
186
|
-
const unionResult = traverse_simple_select_intersect(simple_select_intersect_list[index], context, traverseResult);
|
193
|
+
const unionResult = traverse_simple_select_intersect(simple_select_intersect_list[index], Object.assign(Object.assign({}, context), { fromColumns: context.fromColumns.concat(recursiveColumns) }), traverseResult);
|
187
194
|
columns = columns.map((value, columnIndex) => {
|
188
195
|
const col = Object.assign({ column_name: value.column_name, is_nullable: value.is_nullable || unionResult.columns[columnIndex].is_nullable, table_name: '', table_schema: '', type: value.type }, (value.column_default && { column_default: value.column_default }));
|
189
196
|
return col;
|
@@ -244,14 +251,18 @@ function traverse_simple_select_pramary(simple_select_pramary, context, traverse
|
|
244
251
|
(_d = (_c = simple_select_pramary.group_clause()) === null || _c === void 0 ? void 0 : _c.group_by_list()) === null || _d === void 0 ? void 0 : _d.group_by_item_list().forEach(group_by => {
|
245
252
|
const a_expr = group_by.a_expr();
|
246
253
|
if (a_expr) {
|
254
|
+
newContext.groupBy = true;
|
255
|
+
/* The GROUP BY clause can reference column aliases defined in the SELECT list.
|
256
|
+
There's no need to retrieve nullability or type information from the GROUP BY expressions (findColumn(col). */
|
247
257
|
traverse_a_expr(a_expr, newContext, traverseResult);
|
258
|
+
newContext.groupBy = false;
|
248
259
|
}
|
249
260
|
});
|
250
261
|
const having_expr = (_e = simple_select_pramary.having_clause()) === null || _e === void 0 ? void 0 : _e.a_expr();
|
251
262
|
if (having_expr) {
|
252
263
|
traverse_a_expr(having_expr, newContext, traverseResult);
|
253
264
|
}
|
254
|
-
const filteredColumns = filterColumns_simple_select_pramary(simple_select_pramary,
|
265
|
+
const filteredColumns = filterColumns_simple_select_pramary(simple_select_pramary, Object.assign(Object.assign({}, context), { fromColumns: context.fromColumns.concat(fromResult.columns) }), traverseResult);
|
255
266
|
return {
|
256
267
|
columns: filteredColumns,
|
257
268
|
singleRow: fromResult.singleRow
|
@@ -262,7 +273,7 @@ function extractRelations(a_expr) {
|
|
262
273
|
const relations = columnsRef
|
263
274
|
.map((colRefExpr) => {
|
264
275
|
const colRef = colRefExpr;
|
265
|
-
const tableName = (
|
276
|
+
const tableName = getFieldName(colRef);
|
266
277
|
return tableName;
|
267
278
|
});
|
268
279
|
const uniqueRelations = [...new Set(relations.map(relation => relation.prefix))];
|
@@ -340,7 +351,7 @@ function traverse_target_el(target_el, context, traverseResult) {
|
|
340
351
|
const numParamsBefore = traverseResult.parameters.length;
|
341
352
|
const exprResult = traverse_a_expr(a_expr, context, traverseResult);
|
342
353
|
const colLabel = target_el.colLabel();
|
343
|
-
const alias = colLabel != null ? colLabel
|
354
|
+
const alias = colLabel != null ? get_colid_text(colLabel) : '';
|
344
355
|
if (alias) {
|
345
356
|
(_a = traverseResult.relations) === null || _a === void 0 ? void 0 : _a.forEach(relation => {
|
346
357
|
if ((relation.name === exprResult.table_name || relation.alias === exprResult.table_name)
|
@@ -677,10 +688,16 @@ function traverse_expr_typecast(a_expr_typecast, context, traverseResult) {
|
|
677
688
|
throw Error('traverse_expr_typecast - Not expected:' + a_expr_typecast.getText());
|
678
689
|
}
|
679
690
|
function traverseColumnRef(columnref, fromColumns) {
|
680
|
-
const fieldName = (
|
691
|
+
const fieldName = getFieldName(columnref);
|
681
692
|
const col = findColumn(fieldName, fromColumns);
|
682
693
|
return Object.assign(Object.assign({}, col), { is_nullable: col.is_nullable });
|
683
694
|
}
|
695
|
+
function getFieldName(columnref) {
|
696
|
+
const colid = get_colid_text(columnref.colid());
|
697
|
+
const indirection = columnref.indirection();
|
698
|
+
let fieldName = indirection ? { name: get_indiretion_text(indirection), prefix: colid } : { name: colid, prefix: '' };
|
699
|
+
return fieldName;
|
700
|
+
}
|
684
701
|
function getNameAndTypeIdFromAExprConst(a_expr_const) {
|
685
702
|
var _a, _b;
|
686
703
|
if (a_expr_const.iconst()) {
|
@@ -747,7 +764,7 @@ function traversec_expr(c_expr, context, traverseResult) {
|
|
747
764
|
traverse_select_with_parens(select_with_parens, context, traverseResult);
|
748
765
|
return {
|
749
766
|
column_name: '?column?',
|
750
|
-
is_nullable:
|
767
|
+
is_nullable: false, //empty array
|
751
768
|
table_schema: '',
|
752
769
|
table_name: '',
|
753
770
|
type: 'unknown'
|
@@ -761,6 +778,15 @@ function traversec_expr(c_expr, context, traverseResult) {
|
|
761
778
|
}
|
762
779
|
const columnref = c_expr.columnref();
|
763
780
|
if (columnref) {
|
781
|
+
if (context.groupBy) {
|
782
|
+
return {
|
783
|
+
column_name: columnref.getText(),
|
784
|
+
is_nullable: false,
|
785
|
+
table_name: '',
|
786
|
+
table_schema: '',
|
787
|
+
type: 'unknown'
|
788
|
+
};
|
789
|
+
}
|
764
790
|
if (context.columnRefIsRecord) {
|
765
791
|
const table = (0, select_columns_1.splitTableName)(columnref.getText());
|
766
792
|
const columns = filterColumns(context.fromColumns, table);
|
@@ -898,11 +924,7 @@ function filterColumns(fromColumns, fieldName) {
|
|
898
924
|
});
|
899
925
|
}
|
900
926
|
function excludeColumns(fromColumns, excludeList) {
|
901
|
-
return fromColumns.filter(col =>
|
902
|
-
const found = excludeList.find(excluded => (excluded.prefix === '' || col.table_name === excluded.prefix)
|
903
|
-
&& excluded.name == col.column_name);
|
904
|
-
return !found;
|
905
|
-
});
|
927
|
+
return fromColumns.filter(col => !excludeList.find(excluded => excluded == col.column_name));
|
906
928
|
}
|
907
929
|
function traversec_expr_case(c_expr_case, context, traverseResult) {
|
908
930
|
var _a;
|
@@ -1400,18 +1422,16 @@ function checkLeftJoinIsNullable(leftJoin, subsequentJoins, fromColumns) {
|
|
1400
1422
|
return true; // No INNER JOIN filtered it — remains nullable
|
1401
1423
|
}
|
1402
1424
|
function traverse_table_ref(table_ref, context, traverseResult) {
|
1403
|
-
var _a, _b
|
1425
|
+
var _a, _b;
|
1404
1426
|
const allColumns = [];
|
1405
1427
|
const relation_expr = table_ref.relation_expr();
|
1406
1428
|
const aliasClause = table_ref.alias_clause();
|
1407
|
-
const aliasNameList = (_b = (_a = aliasClause === null || aliasClause === void 0 ? void 0 : aliasClause.name_list()) === null || _a === void 0 ? void 0 : _a.name_list()) === null || _b === void 0 ? void 0 : _b.map(name => name.getText());
|
1408
1429
|
const alias = aliasClause ? aliasClause.colid().getText() : '';
|
1409
1430
|
if (relation_expr) {
|
1410
1431
|
const tableName = traverse_relation_expr(relation_expr);
|
1411
|
-
const fromColumns = getFromColumns(tableName, context.withColumns, context.dbSchema);
|
1432
|
+
const fromColumns = getFromColumns(tableName, context.fromColumns.concat(context.withColumns), context.dbSchema);
|
1412
1433
|
const tableNameWithAlias = alias ? alias : tableName.name;
|
1413
|
-
const
|
1414
|
-
const fromColumnsResult = columnsWithAlias.concat(context.parentColumns);
|
1434
|
+
const fromColumnsResult = fromColumns.map(col => (Object.assign(Object.assign({}, col), { table_name: tableNameWithAlias.toLowerCase() })));
|
1415
1435
|
allColumns.push(...fromColumnsResult);
|
1416
1436
|
if (context.collectNestedInfo) {
|
1417
1437
|
const key = fromColumnsResult.filter(col => col.column_key === 'PRI');
|
@@ -1421,45 +1441,56 @@ function traverse_table_ref(table_ref, context, traverseResult) {
|
|
1421
1441
|
alias: alias,
|
1422
1442
|
renameAs,
|
1423
1443
|
parentRelation: '',
|
1424
|
-
joinColumn: ((
|
1444
|
+
joinColumn: ((_a = key[0]) === null || _a === void 0 ? void 0 : _a.column_name) || '',
|
1425
1445
|
cardinality: 'one',
|
1426
1446
|
parentCardinality: 'one'
|
1427
1447
|
};
|
1428
|
-
(
|
1448
|
+
(_b = traverseResult.relations) === null || _b === void 0 ? void 0 : _b.push(relation);
|
1429
1449
|
}
|
1430
1450
|
if (context.collectDynamicQueryInfo && traverseResult.dynamicQueryInfo.from.length == 0) {
|
1431
1451
|
collectDynamicQueryInfoTableRef(table_ref, null, null, fromColumnsResult, [], traverseResult);
|
1432
1452
|
}
|
1433
1453
|
const joinList = extractJoins(table_ref);
|
1434
|
-
|
1454
|
+
joinList.forEach((join, index) => {
|
1435
1455
|
const joinType = join.joinType; //INNER, LEFT
|
1436
1456
|
const joinQual = join.joinQual;
|
1437
1457
|
const numParamsBefore = traverseResult.parameters.length;
|
1438
|
-
const parentColumns = join.tableRef.LATERAL_P() ? fromColumnsResult : [];
|
1439
|
-
const joinTableRefResult = traverse_table_ref(join.tableRef, Object.assign(Object.assign({}, context), { parentColumns }), traverseResult);
|
1440
|
-
const isLeftJoin = joinType === null || joinType === void 0 ? void 0 : joinType.LEFT();
|
1441
|
-
const filteredColumns = joinQual && (joinQual === null || joinQual === void 0 ? void 0 : joinQual.USING()) ? filterUsingColumns(joinTableRefResult.columns, joinQual) : joinTableRefResult.columns;
|
1442
1458
|
const subsequentJoints = joinList.slice(index + 1);
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
const
|
1447
|
-
|
1448
|
-
}
|
1459
|
+
let joinColumns = [];
|
1460
|
+
if (join.tableRef.LATERAL_P()) {
|
1461
|
+
const newContext = Object.assign(Object.assign({}, context), { parentColumns: allColumns, collectDynamicQueryInfo: false });
|
1462
|
+
const lateralJoinResult = traverse_select_with_parens_or_func_table(join.tableRef, newContext, traverseResult);
|
1463
|
+
joinColumns = lateralJoinResult.columns;
|
1464
|
+
}
|
1465
|
+
else {
|
1466
|
+
const joinTableRefResult = traverse_table_ref(join.tableRef, context, traverseResult);
|
1467
|
+
joinColumns = (joinQual === null || joinQual === void 0 ? void 0 : joinQual.USING()) ? filterUsingColumns(joinTableRefResult.columns, joinQual) : joinTableRefResult.columns;
|
1468
|
+
}
|
1469
|
+
const nullableColumns = (joinType === null || joinType === void 0 ? void 0 : joinType.LEFT())
|
1470
|
+
? joinColumns.map(col => (Object.assign(Object.assign({}, col), { original_is_nullable: col.is_nullable, is_nullable: checkLeftJoinIsNullable(join, subsequentJoints, joinColumns) ? true : col.is_nullable })))
|
1471
|
+
: joinColumns;
|
1472
|
+
allColumns.push(...nullableColumns);
|
1449
1473
|
if (context.collectNestedInfo && joinQual) {
|
1450
|
-
collectNestedInfo(joinQual,
|
1474
|
+
collectNestedInfo(joinQual, joinColumns, traverseResult);
|
1451
1475
|
}
|
1452
1476
|
if (context.collectDynamicQueryInfo) {
|
1453
1477
|
const parameters = traverseResult.parameters.slice(numParamsBefore).map((_, index) => index + numParamsBefore);
|
1454
|
-
collectDynamicQueryInfoTableRef(join.tableRef, joinType, joinQual,
|
1478
|
+
collectDynamicQueryInfoTableRef(join.tableRef, joinType, joinQual, joinColumns, parameters, traverseResult);
|
1455
1479
|
}
|
1456
|
-
return resultColumns;
|
1457
1480
|
});
|
1458
|
-
|
1481
|
+
return {
|
1482
|
+
columns: allColumns,
|
1483
|
+
singleRow: false
|
1484
|
+
};
|
1459
1485
|
}
|
1460
|
-
const
|
1486
|
+
const select_with_parens_or_func_result = traverse_select_with_parens_or_func_table(table_ref, context, traverseResult);
|
1487
|
+
return select_with_parens_or_func_result;
|
1488
|
+
}
|
1489
|
+
function traverse_select_with_parens_or_func_table(tableRef, context, traverseResult) {
|
1490
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
1491
|
+
const func_table = tableRef.func_table();
|
1461
1492
|
if (func_table) {
|
1462
|
-
const funcAlias = ((
|
1493
|
+
const funcAlias = ((_c = (_b = (_a = tableRef.func_alias_clause()) === null || _a === void 0 ? void 0 : _a.alias_clause()) === null || _b === void 0 ? void 0 : _b.colid()) === null || _c === void 0 ? void 0 : _c.getText()) || '';
|
1463
1494
|
const result = traverse_func_table(func_table, context, traverseResult);
|
1464
1495
|
const resultWithAlias = result.columns.map(col => (Object.assign(Object.assign({}, col), { table_name: funcAlias || col.table_name })));
|
1465
1496
|
return {
|
@@ -1467,19 +1498,18 @@ function traverse_table_ref(table_ref, context, traverseResult) {
|
|
1467
1498
|
singleRow: result.singleRow
|
1468
1499
|
};
|
1469
1500
|
}
|
1470
|
-
const select_with_parens =
|
1501
|
+
const select_with_parens = tableRef.select_with_parens();
|
1471
1502
|
if (select_with_parens) {
|
1472
1503
|
const columns = traverse_select_with_parens(select_with_parens, Object.assign(Object.assign({}, context), { collectDynamicQueryInfo: false }), traverseResult);
|
1504
|
+
const aliasNameList = (_f = (_e = (_d = tableRef.alias_clause()) === null || _d === void 0 ? void 0 : _d.name_list()) === null || _e === void 0 ? void 0 : _e.name_list()) === null || _f === void 0 ? void 0 : _f.map(name => name.getText());
|
1505
|
+
const alias = (_h = (_g = tableRef.alias_clause()) === null || _g === void 0 ? void 0 : _g.colid().getText()) !== null && _h !== void 0 ? _h : '';
|
1473
1506
|
const withAlias = columns.columns.map((col, i) => (Object.assign(Object.assign({}, col), { column_name: (aliasNameList === null || aliasNameList === void 0 ? void 0 : aliasNameList[i]) || col.column_name, table_name: alias || col.table_name })));
|
1474
1507
|
return {
|
1475
1508
|
columns: withAlias,
|
1476
1509
|
singleRow: false
|
1477
1510
|
};
|
1478
1511
|
}
|
1479
|
-
|
1480
|
-
columns: allColumns,
|
1481
|
-
singleRow: false
|
1482
|
-
};
|
1512
|
+
throw Error('Stmt not expected:' + tableRef.getText());
|
1483
1513
|
}
|
1484
1514
|
function extractJoins(table_ref) {
|
1485
1515
|
const joinList = [];
|
@@ -1528,7 +1558,7 @@ function getJoinColumns(joinQual) {
|
|
1528
1558
|
const a_expr_or = a_expr_or_list[0];
|
1529
1559
|
const a_expr_and = a_expr_or.a_expr_and_list()[0];
|
1530
1560
|
const columnref = collectContextsOfType(a_expr_and, PostgreSQLParser_1.ColumnrefContext);
|
1531
|
-
const joinColumns = columnref.map(colRef => (
|
1561
|
+
const joinColumns = columnref.map(colRef => getFieldName(colRef));
|
1532
1562
|
return joinColumns;
|
1533
1563
|
}
|
1534
1564
|
return [];
|
@@ -1556,7 +1586,7 @@ function collectNestedInfo(joinQual, resultColumns, traverseResult) {
|
|
1556
1586
|
});
|
1557
1587
|
}
|
1558
1588
|
function filterUsingColumns(fromColumns, joinQual) {
|
1559
|
-
const excludeList = joinQual.name_list().name_list().map(name => (
|
1589
|
+
const excludeList = joinQual.name_list().name_list().map(name => get_colid_text(name.colid()));
|
1560
1590
|
const filteredColumns = excludeColumns(fromColumns, excludeList);
|
1561
1591
|
return filteredColumns;
|
1562
1592
|
}
|
@@ -1649,12 +1679,12 @@ function traverse_relation_expr(relation_expr) {
|
|
1649
1679
|
return name;
|
1650
1680
|
}
|
1651
1681
|
function traverse_qualified_name(qualified_name) {
|
1652
|
-
var _a, _b;
|
1653
1682
|
const colid_name = qualified_name.colid() ? get_colid_text(qualified_name.colid()) : '';
|
1654
|
-
const
|
1655
|
-
if (
|
1683
|
+
const indirection = qualified_name.indirection();
|
1684
|
+
if (indirection) {
|
1685
|
+
const indirection_text = get_indiretion_text(indirection);
|
1656
1686
|
return {
|
1657
|
-
name:
|
1687
|
+
name: indirection_text,
|
1658
1688
|
alias: colid_name
|
1659
1689
|
};
|
1660
1690
|
}
|
@@ -1666,7 +1696,7 @@ function traverse_qualified_name(qualified_name) {
|
|
1666
1696
|
function get_colid_text(colid) {
|
1667
1697
|
const identifier = colid.identifier();
|
1668
1698
|
if (identifier) {
|
1669
|
-
return
|
1699
|
+
return get_identifier_text(identifier);
|
1670
1700
|
}
|
1671
1701
|
const unreserved_keyword = colid.unreserved_keyword();
|
1672
1702
|
if (unreserved_keyword) {
|
@@ -1674,8 +1704,24 @@ function get_colid_text(colid) {
|
|
1674
1704
|
}
|
1675
1705
|
return '';
|
1676
1706
|
}
|
1677
|
-
function
|
1678
|
-
|
1707
|
+
function get_indiretion_text(indirection) {
|
1708
|
+
var _a;
|
1709
|
+
const indirection_el_list = indirection.indirection_el_list();
|
1710
|
+
if (indirection_el_list && indirection_el_list.length === 1) {
|
1711
|
+
const colLabel = (_a = indirection_el_list[0].attr_name()) === null || _a === void 0 ? void 0 : _a.colLabel();
|
1712
|
+
if (colLabel) {
|
1713
|
+
return get_colid_text(colLabel);
|
1714
|
+
}
|
1715
|
+
}
|
1716
|
+
return '';
|
1717
|
+
}
|
1718
|
+
function get_identifier_text(identifier) {
|
1719
|
+
const quoted_identifier = identifier.QuotedIdentifier();
|
1720
|
+
if (quoted_identifier) {
|
1721
|
+
const tableName = quoted_identifier.getText().slice(1, -1);
|
1722
|
+
return tableName;
|
1723
|
+
}
|
1724
|
+
const tableName = identifier.getText();
|
1679
1725
|
return tableName;
|
1680
1726
|
}
|
1681
1727
|
function traverse_unreserved_keyword(unreserved_keyword) {
|
@@ -2052,7 +2098,7 @@ function isNotNull_c_expr(c_expr, field) {
|
|
2052
2098
|
if (c_expr instanceof PostgreSQLParser_1.C_expr_exprContext) {
|
2053
2099
|
const columnref = c_expr.columnref();
|
2054
2100
|
if (columnref) {
|
2055
|
-
const fieldName = (
|
2101
|
+
const fieldName = getFieldName(columnref);
|
2056
2102
|
return (fieldName.name === field.name && (fieldName.prefix === '' || field.prefix === fieldName.prefix));
|
2057
2103
|
}
|
2058
2104
|
const aexprconst = c_expr.aexprconst();
|
@@ -2350,8 +2396,7 @@ function getCheckedUniqueColumn(a_expr_like) {
|
|
2350
2396
|
if (c_expr instanceof PostgreSQLParser_1.C_expr_exprContext) {
|
2351
2397
|
const columnref = c_expr.columnref();
|
2352
2398
|
if (columnref) {
|
2353
|
-
const fieldName = (
|
2354
|
-
// const col = traverseColumnRef(columnref, dbSchema);
|
2399
|
+
const fieldName = getFieldName(columnref);
|
2355
2400
|
return fieldName.name;
|
2356
2401
|
}
|
2357
2402
|
}
|