Skip to content

Commit bde03c8

Browse files
authored
fix: Cherry-pick DH-17599: Change illegal characters to underscores in variable names (#2410)
Cherry-pick #2409 into v0.85: - Add `makeVariableName` method to create valid variable names - Add/update unit tests.
1 parent 2055bc9 commit bde03c8

3 files changed

Lines changed: 79 additions & 16 deletions

File tree

packages/console/src/csv/CsvInputBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class CsvInputBar extends Component<CsvInputBarProps, CsvInputBarState> {
8989
// Set the table name from a file
9090
if (!tableNameSet && file != null && !tableName) {
9191
const dotIndex = file.name.lastIndexOf('.');
92-
const fileTableName = DbNameValidator.legalizeTableName(
92+
const fileTableName = DbNameValidator.makeVariableName(
9393
file.name.substring(0, dotIndex)
9494
);
9595
this.setState({

packages/utils/src/DbNameValidator.test.ts

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,26 @@ import DbNameValidator from './DbNameValidator';
33
const TABLE_PREFIX = 'table_';
44
const COLUMN_PREFIX = 'column_';
55

6-
const VALID_TABLE_NAME = '$+@abc-123_ABC';
7-
const INVALID_TABLE_NAMES = ['%^&ab-c', '-abc', '-'];
8-
const CLEANED_INVALID_TABLE_NAMES = ['ab-c', '-abc', '-'];
6+
const VALID_TABLE_NAMES = ['$+@abc-123_ABC', '$'];
7+
const VARIABLE_NAMES_FROM_VALID = ['___abc_123_ABC', '_'];
8+
9+
const INVALID_TABLE_NAMES = ['%^&ab-c', '-a_b c', '-', '0', '%', ''];
10+
const LEGALIZED_INVALID_TABLE_NAMES = [
11+
'ab-c',
12+
'table_-a_b_c',
13+
'table_-',
14+
'table_0',
15+
'table_0',
16+
'table_0',
17+
];
18+
const VARIABLE_NAMES_FROM_INVALID = [
19+
'ab_c',
20+
'table__a_b_c',
21+
'table__',
22+
'table_0',
23+
'table_0',
24+
'table_0',
25+
];
926

1027
const VALID_COL_NAME = 'abc123_ABC';
1128
const INVALID_COL_NAME = '@abc123_ABC-123';
@@ -16,8 +33,8 @@ const RESERVED_JAVA_WORD = 'return';
1633
const RESERVED_DEEPHAVEN_WORD = 'not';
1734

1835
describe('Table name validation', () => {
19-
it('Returns true on valid table names', () => {
20-
expect(DbNameValidator.isValidTableName(VALID_TABLE_NAME)).toBe(true);
36+
it.each(VALID_TABLE_NAMES)('Returns true on valid table name %s', name => {
37+
expect(DbNameValidator.isValidTableName(name)).toBe(true);
2138
});
2239

2340
it.each(INVALID_TABLE_NAMES)(
@@ -49,14 +66,15 @@ describe('Column name validation', () => {
4966
});
5067

5168
describe('legalizeTableName', () => {
52-
it('Does not change a valid table name', () => {
53-
expect(DbNameValidator.legalizeTableName(VALID_TABLE_NAME)).toBe(
54-
VALID_TABLE_NAME
55-
);
69+
it.each(VALID_TABLE_NAMES)('Does not change a valid table name %s', name => {
70+
expect(DbNameValidator.legalizeTableName(name)).toBe(name);
5671
});
5772

5873
it.each(
59-
INVALID_TABLE_NAMES.map((name, i) => [name, CLEANED_INVALID_TABLE_NAMES[i]])
74+
INVALID_TABLE_NAMES.map((name, i) => [
75+
name,
76+
LEGALIZED_INVALID_TABLE_NAMES[i],
77+
])
6078
)('Legalize an invalid table name %s > %s', (invalid, cleaned) => {
6179
expect(DbNameValidator.legalizeTableName(invalid)).toBe(cleaned);
6280
});
@@ -72,6 +90,25 @@ describe('legalizeTableName', () => {
7290
});
7391
});
7492

93+
describe('makeVariableName', () => {
94+
it.each(
95+
VALID_TABLE_NAMES.map((name, i) => [name, VARIABLE_NAMES_FROM_VALID[i]])
96+
)(
97+
'Makes a variable name for a valid table name %s > %s',
98+
(invalid, variableName) => {
99+
expect(DbNameValidator.makeVariableName(invalid)).toBe(variableName);
100+
}
101+
);
102+
it.each(
103+
INVALID_TABLE_NAMES.map((name, i) => [name, VARIABLE_NAMES_FROM_INVALID[i]])
104+
)(
105+
'Makes a variable name for an invalid table name %s > %s',
106+
(invalid, variableName) => {
107+
expect(DbNameValidator.makeVariableName(invalid)).toBe(variableName);
108+
}
109+
);
110+
});
111+
75112
describe('legalizeColumnName', () => {
76113
it('Does not change a valid column name', () => {
77114
expect(DbNameValidator.legalizeColumnName(VALID_COL_NAME)).toBe(

packages/utils/src/DbNameValidator.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,20 @@ const JAVA_KEYWORDS = new Set([
6262
'false',
6363
]);
6464

65+
// The '$' character is not valid in Deephaven table and column names,
66+
// yet it is treated as valid in the DbNameValidator Java class.
67+
// TODO: Update the regexes once (or if) DH-19169 is merged.
68+
6569
// From io.deephaven.db.tables.utils.DBNameValidator#STERILE_TABLE_AND_NAMESPACE_REGEX
6670
const STERILE_TABLE_AND_NAMESPACE_REGEX = /[^a-zA-Z0-9_$\-+@]/g;
6771

6872
// From io.deephaven.db.tables.utils.DBNameValidator#STERILE_COLUMN_AND_QUERY_REGEX
6973
const STERILE_COLUMN_AND_QUERY_REGEX = /[^A-Za-z0-9_$]/g;
7074

7175
// From io.deephaven.db.tables.utils.DBNameValidator#TABLE_NAME_PATTERN
72-
const TABLE_NAME_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$\-+@]*$/g;
76+
const TABLE_NAME_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$\-+@]*$/;
77+
78+
const STERILE_VARIABLE_NAME_REGEX = /[^a-zA-Z0-9_]/g;
7379

7480
function columnNameReplacer(input: string): string {
7581
// Replace all dashes and spaces with underscores
@@ -108,19 +114,28 @@ class DbNameValidator {
108114
// Remove illegal characters
109115
legalName = legalName.replace(regex, '');
110116

111-
// Check if the name ended up blank
117+
// Check if the name ended up blank and append the prefix.
118+
// Note, io.deephaven.db.tables.utils.DBNameValidator throws an exception in this case.
112119
if (!legalName) {
113-
legalName = prefix + i;
120+
return prefix + i;
114121
}
115122

116-
// If name starts with a number, append prefix to the front
117-
if (!Number.isNaN(Number(legalName.charAt(0)))) {
123+
// If name starts with a number or a dash, append the prefix.
124+
// Note, io.deephaven.db.tables.utils.DBNameValidator throws an exception for names starting with a dash.
125+
if (/^[0-9-]/.test(legalName)) {
118126
legalName = prefix + legalName;
119127
}
120128

121129
return legalName;
122130
};
123131

132+
/**
133+
* Get a legal table name based on the passed in string.
134+
* Follows the same rules as DBNameValidator.java except that here
135+
* we prepend a prefix in cases where the Java class throws an exception.
136+
* @param name The name to legalize
137+
* @returns Legalized table name
138+
*/
124139
static legalizeTableName = (name: string): string =>
125140
DbNameValidator.legalize(
126141
name,
@@ -131,6 +146,17 @@ class DbNameValidator {
131146
0
132147
);
133148

149+
/**
150+
* Make a valid variable name based on the passed in string.
151+
* @param name The name to get the variable name for
152+
* @returns Variable name
153+
*/
154+
static makeVariableName = (name: string): string =>
155+
DbNameValidator.legalizeTableName(name).replace(
156+
STERILE_VARIABLE_NAME_REGEX,
157+
'_'
158+
);
159+
134160
static legalizeColumnNames = (headers: string[]): string[] => {
135161
const legalHeaders: string[] = [];
136162
headers.forEach((header, i) => {

0 commit comments

Comments
 (0)