@sap/cds-compiler
Version:
CDS (Core Data Services) compiler and backends
102 lines (90 loc) • 3.76 kB
JavaScript
// API functions returning the SQL identifier token text for a name
// The CDS compiler has different SQL “naming modes” with the following intentions:
//
// - the recommended 'plain': as often as possible, use a regular identifier
// (i.e. without surrounding quotes) which looks the same (modulo prior `.`
// to `_` replacements) as the name used in CDS.
// - not provided (should have been the only alternative to 'plain'): construct
// an identifier such that the effective name (the one in the DB schema) is
// the same as the name used in CDS.
// - 'quoted' and 'hdbcds' for HANA only: similar to the non-provided previous
// mode, with different adaptations to HANA CDS and XS (classic) restrictions.
// The main objective of this file is to support the 'plain' mode in a “smart”
// manner. If we would use the CDS name (after `.` to `_` replacements)
// directly as the identifier token text, the deployment would fail if names
// had been used in a model which were “reserved” on the target database.
//
// Using the provided function smartId() instead of an identity function avoids
// this situation: it constructs delimited identifiers for the reserved names.
// Other names are returned directly to avoid that people think that they
// had to use all-upper names in CDS.
// Please note that `.` to `_` replacements (and similar replacements for the
// naming mode 'hdbcds') had to be done before calling these functions.
// The main function can be used for:
// - table and view names,
// - query source aliases (“correlation names”),
// - column and association names, column aliases,
// - constraint names,
// - NOT: function names - use smartFuncId() instead,
// - NOT: parameter names,
// - NOT: data types (we render builtins only anyway).
;
const keywords = require( './base/keywords' );
const sqlDialects = {
plain: {},
h2: {
// See http://www.h2database.com/html/grammar.html#name
regularRegex: /^[A-Za-z_][A-Za-z_0-9]*$/,
reservedWords: keywords.h2,
effectiveName: name => name.toUpperCase(),
asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
},
sqlite: {
regularRegex: /^[A-Za-z_][A-Za-z_$0-9]*$/,
reservedWords: keywords.sqlite,
effectiveName: name => name,
asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
},
postgres: {
regularRegex: /^[A-Za-z_][A-Za-z_$0-9]*$/,
reservedWords: keywords.postgres,
effectiveName: name => name.toLowerCase(),
asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
},
hana: {
regularRegex: /^[A-Za-z_][A-Za-z_$#0-9]*$/,
reservedWords: keywords.hana,
effectiveName: name => name.toUpperCase(),
asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
},
hdbcds: {
regularRegex: /^[A-Za-z_][A-Za-z_0-9]*$/,
reservedWords: keywords.hdbcds,
effectiveName: name => name,
asDelimitedId: name => `"${ name.replace(/"/g, '""') }"`,
},
};
function smartId( name, dialect ) {
const s = (typeof dialect === 'string')
? sqlDialects[dialect]
: dialect;
if (s.regularRegex && !s.regularRegex.test( name ) ||
s.reservedWords && s.reservedWords.includes( name.toUpperCase() ))
return s.asDelimitedId( s.effectiveName( name ) );
return name;
}
function smartFuncId( name, dialect ) {
const s = (typeof dialect === 'string')
? sqlDialects[dialect]
: dialect;
if (s.regularRegex && !s.regularRegex.test( name ))
return s.asDelimitedId( s.effectiveName( name ) );
return name;
}
function delimitedId( name, dialect ) {
const s = (typeof dialect === 'string')
? sqlDialects[dialect]
: dialect;
return s.asDelimitedId( name );
}
module.exports = { smartId, smartFuncId, delimitedId };