sqljson-query
Advanced tools
Comparing version 1.9.1 to 1.9.2
@@ -16,3 +16,3 @@ import { Nullable } from './util/mod'; | ||
resultRepresentations?: Nullable<ResultRepr[]>; | ||
additionalObjectPropertyColumns?: Nullable<AdditionalObjectPropertyColumn[]>; | ||
additionalOutputColumns?: Nullable<AdditionalOutputColumn[]>; | ||
generateResultTypes?: Nullable<boolean>; | ||
@@ -24,2 +24,3 @@ generateSource?: Nullable<boolean>; | ||
} | ||
export type AdditionalOutputColumn = string | TableFieldExpr; | ||
export type QueryTypesFileHeader = string | { | ||
@@ -69,3 +70,3 @@ [l in SourceLanguage]: string; | ||
referenceName: undefined; | ||
subqueryAlias?: Nullable<string>; | ||
fromEntryAlias?: Nullable<string>; | ||
} | ||
@@ -96,6 +97,2 @@ export interface ChildSpec extends TableJsonSpec { | ||
export type ResultRepr = "MULTI_COLUMN_ROWS" | "JSON_OBJECT_ROWS" | "JSON_ARRAY_ROW"; | ||
export type AdditionalObjectPropertyColumn = string | { | ||
property: string; | ||
as: string; | ||
}; | ||
export declare function getInlineParentSpecs(tableSpec: TableJsonSpec): InlineParentSpec[]; | ||
@@ -119,3 +116,3 @@ export declare function getReferencedParentSpecs(tableSpec: TableJsonSpec): ReferencedParentSpec[]; | ||
defaultSchema: Nullable<string>, dbmd: DatabaseMetadata, specLoc: SpecLocation): RelId; | ||
export declare function verifyTableFieldExpressionsValid(tableSpec: TableJsonSpec, defaultSchema: Nullable<string>, dbmd: DatabaseMetadata, specLoc: SpecLocation): void; | ||
export declare function verifyTableFieldExpressionsValid(fieldExpressions: Nullable<(string | TableFieldExpr)[]>, table: string, defaultSchema: Nullable<string>, dbmd: DatabaseMetadata, specLoc: SpecLocation): void; | ||
export declare function validateCustomMatchCondition(customMatchCond: CustomMatchCondition, childRelId: RelId, parentRelId: RelId, dbmd: DatabaseMetadata, specLoc: SpecLocation): void; |
@@ -50,7 +50,7 @@ "use strict"; | ||
exports.identifyTable = identifyTable; | ||
function verifyTableFieldExpressionsValid(tableSpec, defaultSchema, dbmd, specLoc) { | ||
if (!tableSpec.fieldExpressions) | ||
function verifyTableFieldExpressionsValid(fieldExpressions, table, defaultSchema, dbmd, specLoc) { | ||
if (!fieldExpressions) | ||
return; | ||
const simpleSelectFields = []; | ||
for (const [ix, fieldExpr] of tableSpec.fieldExpressions.entries()) { | ||
for (const [ix, fieldExpr] of fieldExpressions.entries()) { | ||
if (typeof fieldExpr === 'string') | ||
@@ -69,3 +69,3 @@ simpleSelectFields.push(fieldExpr); | ||
} | ||
const relMd = getRelMetadata(tableSpec.table, defaultSchema, dbmd, specLoc); | ||
const relMd = getRelMetadata(table, defaultSchema, dbmd, specLoc); | ||
verifyFieldsExistInRelMd(simpleSelectFields, relMd, dbmd, specLoc); | ||
@@ -93,5 +93,2 @@ } | ||
} | ||
function verifyPropertyColumnsExist(propertyNames, specLoc) { | ||
// TODO | ||
} | ||
function getRelMetadata(table, // as from input, possibly qualified | ||
@@ -98,0 +95,0 @@ defaultSchema, dbmd, specLoc) { |
@@ -9,7 +9,7 @@ import { Nullable } from '../../util/nullable'; | ||
constructor(indentSpaces: number); | ||
getRowObjectExpression(selectEntries: SelectEntry[], srcAlias: string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], orderBy: Nullable<string>, srcAlias: string): string; | ||
getAggregatedColumnValuesExpression(selectEntry: SelectEntry, orderBy: Nullable<string>, srcAlias: string): string; | ||
getRowObjectExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, orderBy: Nullable<string>): string; | ||
getAggregatedColumnValuesExpression(valueExpression: string, orderBy: Nullable<string>): string; | ||
quoteObjectNameIfNeeded(name: string): string; | ||
quoteColumnNameIfNeeded(nameExact: string): string; | ||
} |
@@ -5,6 +5,3 @@ "use strict"; | ||
const strings_1 = require("../../util/strings"); | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const sqlKeywordsLowercase = new Set([ | ||
'select', 'from', 'where', 'user', 'order', 'group', 'by', 'over', 'is' | ||
]); | ||
const sql_specs_1 = require("../sql-specs"); | ||
class HSQLDialect { | ||
@@ -16,6 +13,6 @@ constructor(indentSpaces) { | ||
} | ||
getRowObjectExpression(selectEntries, srcAlias) { | ||
getRowObjectExpression(selectEntries, selectEntryValueSqlFn) { | ||
const objectFieldDecls = selectEntries | ||
.map(se => `'${se.projectedName}' value ${srcAlias}.${this.quoteColumnNameIfNeeded(se.projectedName)}` + | ||
(needsFormatJsonQualifier(se) ? " format json" : "")) | ||
.map(se => `'${se.projectedName}' value ${selectEntryValueSqlFn(se)}` + | ||
(needsFormatJson(se) ? " format json" : "")) | ||
.join(',\n'); | ||
@@ -26,13 +23,12 @@ return ('json_object(\n' + | ||
} | ||
getAggregatedRowObjectsExpression(selectEntries, orderBy, srcAlias) { | ||
getAggregatedRowObjectsExpression(selectEntries, selectEntryValueSqlFn, orderBy) { | ||
return ('coalesce(json_arrayagg(' + | ||
this.getRowObjectExpression(selectEntries, srcAlias) + | ||
(orderBy != null ? ' order by ' + orderBy.replace(/\$\$/g, srcAlias) : '') + | ||
this.getRowObjectExpression(selectEntries, selectEntryValueSqlFn) + | ||
(orderBy != null ? ` order by ${orderBy}` : '') + | ||
`), '[]')`); | ||
} | ||
getAggregatedColumnValuesExpression(selectEntry, orderBy, srcAlias) { | ||
const columnName = selectEntry.projectedName; | ||
return (`coalesce(json_arrayagg(${srcAlias}.${this.quoteColumnNameIfNeeded(columnName)}` + | ||
(orderBy != null ? ` order by ${orderBy.replace(/\$\$/g, srcAlias)}` : '') + | ||
`), '[]')`); | ||
getAggregatedColumnValuesExpression(valueExpression, orderBy) { | ||
return (`coalesce(jsonb_agg(${valueExpression}` + | ||
(orderBy != null ? ' order by ' + orderBy : '') + | ||
'))'); | ||
} | ||
@@ -44,3 +40,3 @@ quoteObjectNameIfNeeded(name) { | ||
!this.uppercaseNameRegex.test(name) || | ||
sqlKeywordsLowercase.has(name.toLowerCase())) | ||
sql_specs_1.generalSqlKeywordsLowercase.has(name.toLowerCase())) | ||
return `"${name}"`; | ||
@@ -52,3 +48,5 @@ return name; | ||
return nameExact; | ||
if (!simpleIdentifierRegex.test(nameExact) || !this.uppercaseNameRegex.test(nameExact)) | ||
if (!simpleIdentifierRegex.test(nameExact) || | ||
!this.uppercaseNameRegex.test(nameExact) || | ||
avoidColumnNamesLowercase.has(nameExact.toLowerCase())) | ||
return `"${nameExact}"`; | ||
@@ -59,3 +57,3 @@ return nameExact; | ||
exports.HSQLDialect = HSQLDialect; | ||
function needsFormatJsonQualifier(selectEntry) { | ||
function needsFormatJson(selectEntry) { | ||
switch (selectEntry.entryType) { | ||
@@ -70,5 +68,84 @@ case "se-parent-ref": | ||
case "se-inline-parent-prop": | ||
return needsFormatJsonQualifier(selectEntry.parentSelectEntry); | ||
return needsFormatJson(selectEntry.parentSelectEntry); | ||
} | ||
} | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const avoidColumnNamesLowercase = new Set([ | ||
'all', | ||
'and', | ||
'and', | ||
'any', | ||
'as', | ||
'at', | ||
'between', | ||
'both', | ||
'by', | ||
'call', | ||
'case', | ||
'cast', | ||
'coalesce', | ||
'collation', | ||
'convert', | ||
'corresponding', | ||
'create', | ||
'cross', | ||
'cube', | ||
'current', | ||
'current_path', | ||
'default', | ||
'distinct', | ||
'do', | ||
'drop', | ||
'else', | ||
'every', | ||
'except', | ||
'exists', | ||
'fetch', | ||
'for', | ||
'from', | ||
'full', | ||
'grant', | ||
'group', | ||
'grouping', | ||
'having', | ||
'in', | ||
'inner', | ||
'intersect', | ||
'into', | ||
'is', | ||
'join', | ||
'leading', | ||
'left', | ||
'like', | ||
'natural', | ||
'normalize', | ||
'not', | ||
'nullif', | ||
'on', | ||
'or', | ||
'order', | ||
'outer', | ||
'primary', | ||
'references', | ||
'right', | ||
'rollup', | ||
'row', | ||
'select', | ||
'set', | ||
'some', | ||
'sum', | ||
'table', | ||
'then', | ||
'to', | ||
'trailing', | ||
'trigger', | ||
'union', | ||
'unique', | ||
'user', | ||
'using', | ||
'values', | ||
'when', | ||
'where', | ||
'with', | ||
]); | ||
//# sourceMappingURL=hsql.js.map |
@@ -9,7 +9,7 @@ import { Nullable } from '../../util/nullable'; | ||
constructor(indentSpaces: number); | ||
getRowObjectExpression(selectEntries: SelectEntry[], srcAlias: string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], orderBy: Nullable<string>, srcAlias: string): string; | ||
getAggregatedColumnValuesExpression(selectEntry: SelectEntry, orderBy: Nullable<string>, srcAlias: string): string; | ||
getRowObjectExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, orderBy: Nullable<string>): string; | ||
getAggregatedColumnValuesExpression(valueExpression: string, orderBy: Nullable<string>): string; | ||
quoteObjectNameIfNeeded(name: string): string; | ||
quoteColumnNameIfNeeded(name: string): string; | ||
} |
@@ -5,6 +5,3 @@ "use strict"; | ||
const strings_1 = require("../../util/strings"); | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const sqlKeywordsLowercase = new Set([ | ||
'select', 'from', 'where', 'user', 'order', 'group', 'by', 'over', 'is' | ||
]); | ||
const sql_specs_1 = require("../sql-specs"); | ||
class MySQLDialect { | ||
@@ -16,6 +13,5 @@ constructor(indentSpaces) { | ||
} | ||
getRowObjectExpression(selectEntries, srcAlias) { | ||
const columnNames = selectEntries.map(e => e.projectedName); | ||
const objectFieldDecls = columnNames | ||
.map(colName => `'${colName}', ${srcAlias}.${this.quoteColumnNameIfNeeded(colName)}`) | ||
getRowObjectExpression(selectEntries, selectEntryValueSqlFn) { | ||
const objectFieldDecls = selectEntries | ||
.map(se => `'${se.projectedName}', ${selectEntryValueSqlFn(se)}`) | ||
.join(',\n'); | ||
@@ -26,14 +22,13 @@ return ('json_object(\n' + | ||
} | ||
getAggregatedRowObjectsExpression(selectEntries, orderBy, srcAlias) { | ||
getAggregatedRowObjectsExpression(selectEntries, selectEntryValueSqlFn, orderBy) { | ||
if (orderBy != null) | ||
throw new Error(`MySQL dialect does not support ordering in aggregate functions currently.`); | ||
return ('cast(coalesce(json_arrayagg(' + | ||
this.getRowObjectExpression(selectEntries, srcAlias) + | ||
this.getRowObjectExpression(selectEntries, selectEntryValueSqlFn) + | ||
`), json_type('[]')) as json)`); | ||
} | ||
getAggregatedColumnValuesExpression(selectEntry, orderBy, srcAlias) { | ||
const columnName = selectEntry.projectedName; | ||
getAggregatedColumnValuesExpression(valueExpression, orderBy) { | ||
if (orderBy != null) | ||
throw new Error(`Error for column ${columnName}: MySQL dialect does not support ordering in aggregate functions currently.`); | ||
return (`cast(coalesce(json_arrayagg(${srcAlias}.${this.quoteColumnNameIfNeeded(columnName)}), json_type('[]')) as json)`); | ||
throw new Error(`MySQL dialect does not support ordering in aggregate functions currently.`); | ||
return (`cast(coalesce(json_arrayagg(${valueExpression}), json_type('[]')) as json)`); | ||
} | ||
@@ -45,3 +40,4 @@ quoteObjectNameIfNeeded(name) { | ||
return `\`${(0, strings_1.unDoubleQuote)(name)}\``; | ||
if (!simpleIdentifierRegex.test(name) || sqlKeywordsLowercase.has(name.toLowerCase())) | ||
if (!simpleIdentifierRegex.test(name) || | ||
sql_specs_1.generalSqlKeywordsLowercase.has(name.toLowerCase())) | ||
return `\`${name}\``; | ||
@@ -55,3 +51,4 @@ return name; | ||
return `\`${(0, strings_1.unDoubleQuote)(name)}\``; | ||
if (!simpleIdentifierRegex.test(name)) | ||
if (!simpleIdentifierRegex.test(name) || | ||
avoidColumnNamesLowercase.has(name.toLowerCase())) | ||
return `\`${name}\``; | ||
@@ -62,2 +59,248 @@ return name; | ||
exports.MySQLDialect = MySQLDialect; | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const avoidColumnNamesLowercase = new Set([ | ||
'add', | ||
'all', | ||
'alter', | ||
'analyze', | ||
'and', | ||
'as', | ||
'asc', | ||
'asensitive', | ||
'before', | ||
'between', | ||
'bigint', | ||
'binary', | ||
'blob', | ||
'both', | ||
'by', | ||
'call', | ||
'cascade', | ||
'case', | ||
'change', | ||
'char', | ||
'character', | ||
'check', | ||
'collate', | ||
'column', | ||
'condition', | ||
'constraint', | ||
'continue', | ||
'convert', | ||
'create', | ||
'cross', | ||
'cube', | ||
'cume_dist', | ||
'current_date', | ||
'current_time', | ||
'current_timestamp', | ||
'current_user', | ||
'cursor', | ||
'database', | ||
'databases', | ||
'day_hour', | ||
'day_microsecond', | ||
'day_minute', | ||
'day_second', | ||
'dec', | ||
'decimal', | ||
'declare', | ||
'default', | ||
'delayed', | ||
'delete', | ||
'dense_rank', | ||
'desc', | ||
'describe', | ||
'deterministic', | ||
'distinct', | ||
'distinctrow', | ||
'div', | ||
'double', | ||
'drop', | ||
'dual', | ||
'each', | ||
'else', | ||
'elseif', | ||
'enclosed', | ||
'escaped', | ||
'except', | ||
'exists', | ||
'exit', | ||
'explain', | ||
'false', | ||
'fetch', | ||
'float', | ||
'float4', | ||
'float8', | ||
'for', | ||
'force', | ||
'foreign', | ||
'from', | ||
'fulltext', | ||
'function', | ||
'generated', | ||
'get', | ||
'grant', | ||
'group', | ||
'grouping', | ||
'having', | ||
'high_priority', | ||
'hour_microsecond', | ||
'hour_minute', | ||
'hour_second', | ||
'if', | ||
'ignore', | ||
'in', | ||
'index', | ||
'infile', | ||
'inner', | ||
'inout', | ||
'insensitive', | ||
'insert', | ||
'int', | ||
'int1', | ||
'int2', | ||
'int3', | ||
'int4', | ||
'int8', | ||
'integer', | ||
'interval', | ||
'into', | ||
'is', | ||
'iterate', | ||
'join', | ||
'key', | ||
'keys', | ||
'kill', | ||
'lateral', | ||
'lead', | ||
'leading', | ||
'leave', | ||
'left', | ||
'like', | ||
'limit', | ||
'lines', | ||
'load', | ||
'localtime', | ||
'localtimestamp', | ||
'lock', | ||
'long', | ||
'longblob', | ||
'longtext', | ||
'loop', | ||
'low_priority', | ||
'match', | ||
'mediumblob', | ||
'mediumint', | ||
'mediumtext', | ||
'middleint', | ||
'minute_microsecond', | ||
'minute_second', | ||
'mod', | ||
'modifies', | ||
'natural', | ||
'no_write_to_binlog', | ||
'not', | ||
'null', | ||
'numeric', | ||
'of', | ||
'on', | ||
'optimize', | ||
'option', | ||
'optionally', | ||
'or', | ||
'order', | ||
'out', | ||
'outer', | ||
'outfile', | ||
'over', | ||
'partition', | ||
'percent_rank', | ||
'precision', | ||
'primary', | ||
'procedure', | ||
'purge', | ||
'range', | ||
'rank', | ||
'read', | ||
'reads', | ||
'real', | ||
'recursive', | ||
'references', | ||
'regexp', | ||
'release', | ||
'rename', | ||
'repeat', | ||
'replace', | ||
'require', | ||
'require', | ||
'resignal', | ||
'restrict', | ||
'return', | ||
'revoke', | ||
'right', | ||
'rlike', | ||
'row', | ||
'row_number', | ||
'rows', | ||
'schema', | ||
'schemas', | ||
'second_microsecond', | ||
'select', | ||
'sensitive', | ||
'separator', | ||
'set', | ||
'signal', | ||
'show', | ||
'smallint', | ||
'spatial', | ||
'specific', | ||
'sql', | ||
'sql_big_result', | ||
'sql_calc_found_rows', | ||
'sql_small_result', | ||
'sqlexception', | ||
'sqlstate', | ||
'sqlwarning', | ||
'ssl', | ||
'starting', | ||
'straight_join', | ||
'system', | ||
'table', | ||
'terminated', | ||
'then', | ||
'tinyblob', | ||
'tinyint', | ||
'tinytext', | ||
'to', | ||
'trailing', | ||
'trigger', | ||
'true', | ||
'undo', | ||
'union', | ||
'unique', | ||
'unlock', | ||
'unsigned', | ||
'update', | ||
'usage', | ||
'use', | ||
'using', | ||
'utc_date', | ||
'utc_time', | ||
'utc_timestamp', | ||
'values', | ||
'varbinary', | ||
'varchar', | ||
'varcharacter', | ||
'varying', | ||
'when', | ||
'where', | ||
'while', | ||
'window', | ||
'with', | ||
'write', | ||
'xor', | ||
'year_month', | ||
'zerofill' | ||
]); | ||
//# sourceMappingURL=mysql.js.map |
@@ -9,7 +9,7 @@ import { Nullable } from '../../util/nullable'; | ||
constructor(indentSpaces: number); | ||
getRowObjectExpression(selectEntries: SelectEntry[], srcAlias: string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], orderBy: Nullable<string>, srcAlias: string): string; | ||
getAggregatedColumnValuesExpression(selectEntry: SelectEntry, orderBy: Nullable<string>, srcAlias: string): string; | ||
getRowObjectExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, orderBy: Nullable<string>): string; | ||
getAggregatedColumnValuesExpression(valueExpression: string, orderBy: Nullable<string>): string; | ||
quoteObjectNameIfNeeded(name: string): string; | ||
quoteColumnNameIfNeeded(nameExact: string): string; | ||
quoteColumnNameIfNeeded(name: string): string; | ||
} |
@@ -5,6 +5,3 @@ "use strict"; | ||
const strings_1 = require("../../util/strings"); | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const sqlKeywordsLowercase = new Set([ | ||
'select', 'from', 'where', 'user', 'order', 'group', 'by', 'over', 'is' | ||
]); | ||
const sql_specs_1 = require("../sql-specs"); | ||
class OracleDialect { | ||
@@ -16,6 +13,5 @@ constructor(indentSpaces) { | ||
} | ||
getRowObjectExpression(selectEntries, srcAlias) { | ||
const columnNames = selectEntries.map(e => e.projectedName); | ||
const objectFieldDecls = columnNames | ||
.map(colName => `'${colName}' value ${srcAlias}.${this.quoteColumnNameIfNeeded(colName)}`) | ||
getRowObjectExpression(selectEntries, selectEntryValueSqlFn) { | ||
const objectFieldDecls = selectEntries | ||
.map(se => `'${se.projectedName}' value ${selectEntryValueSqlFn(se)}`) | ||
.join(',\n'); | ||
@@ -26,12 +22,11 @@ return ('json_object(\n' + | ||
} | ||
getAggregatedRowObjectsExpression(selectEntries, orderBy, srcAlias) { | ||
getAggregatedRowObjectsExpression(selectEntries, selectEntryValueSqlFn, orderBy) { | ||
return ('treat(coalesce(json_arrayagg(' + | ||
this.getRowObjectExpression(selectEntries, srcAlias) + | ||
(orderBy != null ? ' order by ' + orderBy.replace(/\$\$/g, srcAlias) : '') + | ||
this.getRowObjectExpression(selectEntries, selectEntryValueSqlFn) + | ||
(orderBy != null ? ` order by ${orderBy}` : '') + | ||
` returning clob), to_clob('[]')) as json)`); | ||
} | ||
getAggregatedColumnValuesExpression(selectEntry, orderBy, srcAlias) { | ||
const columnName = selectEntry.projectedName; | ||
return (`treat(coalesce(json_arrayagg(${srcAlias}.${this.quoteColumnNameIfNeeded(columnName)}` + | ||
(orderBy != null ? ` order by ${orderBy.replace(/\$\$/g, srcAlias)}` : '') + | ||
getAggregatedColumnValuesExpression(valueExpression, orderBy) { | ||
return (`treat(coalesce(json_arrayagg(${valueExpression}` + | ||
(orderBy != null ? ` order by ${orderBy}` : '') + | ||
` returning clob), to_clob('[]')) as json)`); | ||
@@ -44,15 +39,128 @@ } | ||
!this.uppercaseNameRegex.test(name) || | ||
sqlKeywordsLowercase.has(name.toLowerCase())) | ||
sql_specs_1.generalSqlKeywordsLowercase.has(name.toLowerCase())) | ||
return `"${name}"`; | ||
return name; | ||
} | ||
quoteColumnNameIfNeeded(nameExact) { | ||
if (this.quotedStringRegex.test(nameExact)) | ||
return nameExact; | ||
if (!simpleIdentifierRegex.test(nameExact) || !this.uppercaseNameRegex.test(nameExact)) | ||
return `"${nameExact}"`; | ||
return nameExact; | ||
quoteColumnNameIfNeeded(name) { | ||
if (this.quotedStringRegex.test(name)) | ||
return name; | ||
if (!simpleIdentifierRegex.test(name) || | ||
!this.uppercaseNameRegex.test(name) || | ||
avoidColumnNamesLowercase.has(name.toLowerCase())) | ||
return `"${name}"`; | ||
return name; | ||
} | ||
} | ||
exports.OracleDialect = OracleDialect; | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const avoidColumnNamesLowercase = new Set([ | ||
'access', | ||
'add', | ||
'all', | ||
'alter', | ||
'and', | ||
'any', | ||
'as', | ||
'asc', | ||
'audit', | ||
'between', | ||
'by', | ||
'char', | ||
'check', | ||
'cluster', | ||
'column', | ||
'comment', | ||
'compress', | ||
'connect', | ||
'create', | ||
'current', | ||
'date', | ||
'decimal', | ||
'default', | ||
'delete', | ||
'desc', | ||
'distinct', | ||
'drop', | ||
'else', | ||
'exclusive', | ||
'exists', | ||
'file', | ||
'float', | ||
'for', | ||
'from', | ||
'grant', | ||
'group', | ||
'having', | ||
'identified', | ||
'immediate', | ||
'in', | ||
'increment', | ||
'index', | ||
'initial', | ||
'insert', | ||
'integer', | ||
'intersect', | ||
'into', | ||
'is', | ||
'level', | ||
'like', | ||
'lock', | ||
'long', | ||
'maxextents', | ||
'minus', | ||
'mlslabel', | ||
'mode', | ||
'modify', | ||
'noaudit', | ||
'nocompress', | ||
'not', | ||
'nowait', | ||
'null', | ||
'number', | ||
'of', | ||
'offline', | ||
'on', | ||
'online', | ||
'option', | ||
'or', | ||
'order', | ||
'pctfree', | ||
'prior', | ||
'public', | ||
'raw', | ||
'rename', | ||
'resource', | ||
'revoke', | ||
'row', | ||
'rowid', | ||
'rownum', | ||
'rows', | ||
'select', | ||
'session', | ||
'set', | ||
'share', | ||
'size', | ||
'smallint', | ||
'start', | ||
'successful', | ||
'synonym', | ||
'sysdate', | ||
'table', | ||
'then', | ||
'to', | ||
'trigger', | ||
'uid', | ||
'union', | ||
'unique', | ||
'update', | ||
'user', | ||
'validate', | ||
'values', | ||
'varchar', | ||
'varchar2', | ||
'view', | ||
'whenever', | ||
'where', | ||
'with', | ||
]); | ||
//# sourceMappingURL=ora.js.map |
@@ -9,7 +9,7 @@ import { Nullable } from '../../util/nullable'; | ||
constructor(indentSpaces: number); | ||
getRowObjectExpression(selectEntries: SelectEntry[], srcAlias: string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], orderBy: Nullable<string>, srcAlias: string): string; | ||
getAggregatedColumnValuesExpression(selectEntry: SelectEntry, orderBy: Nullable<string>, srcAlias: string): string; | ||
getRowObjectExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, orderBy: Nullable<string>): string; | ||
getAggregatedColumnValuesExpression(valueExpression: string, orderBy: Nullable<string>): string; | ||
quoteObjectNameIfNeeded(name: string): string; | ||
quoteColumnNameIfNeeded(name: string): string; | ||
} |
@@ -5,6 +5,3 @@ "use strict"; | ||
const strings_1 = require("../../util/strings"); | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const sqlKeywordsLowercase = new Set([ | ||
'select', 'from', 'where', 'user', 'order', 'group', 'by', 'over', 'is' | ||
]); | ||
const sql_specs_1 = require("../sql-specs"); | ||
class PostgresDialect { | ||
@@ -16,6 +13,5 @@ constructor(indentSpaces) { | ||
} | ||
getRowObjectExpression(selectEntries, srcAlias) { | ||
const columnNames = selectEntries.map(e => e.projectedName); | ||
const objectFieldDecls = columnNames | ||
.map(colName => `'${colName}', ${srcAlias}.${this.quoteColumnNameIfNeeded(colName)}`) | ||
getRowObjectExpression(selectEntries, selectEntryValueSqlFn) { | ||
const objectFieldDecls = selectEntries | ||
.map(se => `'${se.projectedName}', ${selectEntryValueSqlFn(se)}`) | ||
.join(',\n'); | ||
@@ -26,12 +22,11 @@ return ('jsonb_build_object(\n' + | ||
} | ||
getAggregatedRowObjectsExpression(selectEntries, orderBy, srcAlias) { | ||
getAggregatedRowObjectsExpression(selectEntries, selectEntryValueSqlFn, orderBy) { | ||
return ('coalesce(jsonb_agg(' + | ||
this.getRowObjectExpression(selectEntries, srcAlias) + | ||
(orderBy != null ? ` order by ${orderBy.replace(/\$\$/g, srcAlias)}` : '') + | ||
this.getRowObjectExpression(selectEntries, selectEntryValueSqlFn) + | ||
(orderBy != null ? ` order by ${orderBy}` : '') + | ||
`),'[]'::jsonb)`); | ||
} | ||
getAggregatedColumnValuesExpression(selectEntry, orderBy, srcAlias) { | ||
const columnName = selectEntry.projectedName; | ||
return (`coalesce(jsonb_agg(${srcAlias}.${this.quoteColumnNameIfNeeded(columnName)}` + | ||
(orderBy != null ? ' order by ' + orderBy.replace(/\$\$/g, srcAlias) : '') + | ||
getAggregatedColumnValuesExpression(valueExpression, orderBy) { | ||
return (`coalesce(jsonb_agg(${valueExpression}` + | ||
(orderBy != null ? ' order by ' + orderBy : '') + | ||
'))'); | ||
@@ -44,3 +39,3 @@ } | ||
!this.lowercaseNameRegex.test(name) || | ||
sqlKeywordsLowercase.has(name)) | ||
sql_specs_1.generalSqlKeywordsLowercase.has(name.toLowerCase())) | ||
return `"${name}"`; | ||
@@ -52,3 +47,5 @@ return name; | ||
return name; | ||
if (!simpleIdentifierRegex.test(name) || !this.lowercaseNameRegex.test(name)) | ||
if (!simpleIdentifierRegex.test(name) || | ||
!this.lowercaseNameRegex.test(name) || | ||
avoidColumnNamesLowercase.has(name.toLowerCase())) | ||
return `"${name}"`; | ||
@@ -59,2 +56,98 @@ return name; | ||
exports.PostgresDialect = PostgresDialect; | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const avoidColumnNamesLowercase = new Set([ | ||
'all', | ||
'analyze', | ||
'and', | ||
'any', | ||
'array', | ||
'as', | ||
'asymmetric', | ||
'authorization', | ||
'binary', | ||
'both', | ||
'case', | ||
'cast', | ||
'check', | ||
'collate', | ||
'collation', | ||
'column', | ||
'constraint', | ||
'create', | ||
'cross', | ||
'current_date', | ||
'current_role', | ||
'current_time', | ||
'current_timestamp', | ||
'current_user', | ||
'default', | ||
'deferrable', | ||
'desc', | ||
'distinct', | ||
'do', | ||
'else', | ||
'end', | ||
'except', | ||
'false', | ||
'fetch', | ||
'for', | ||
'foreign', | ||
'freeze', | ||
'from', | ||
'full', | ||
'grant', | ||
'group', | ||
'having', | ||
'ilike', | ||
'in', | ||
'initially', | ||
'inner', | ||
'intersect', | ||
'interval', | ||
'into', | ||
'is', | ||
'isnull', | ||
'join', | ||
'lateral', | ||
'leading', | ||
'left', | ||
'like', | ||
'limit', | ||
'localtime', | ||
'localtimestamp', | ||
'natural', | ||
'not', | ||
'notnull', | ||
'null', | ||
'offset', | ||
'on', | ||
'only', | ||
'or', | ||
'order', | ||
'outer', | ||
'overlaps', | ||
'placing', | ||
'primary', | ||
'references', | ||
'right', | ||
'select', | ||
'session_user', | ||
'similar', | ||
'some', | ||
'symmetric', | ||
'table', | ||
'tablesample', | ||
'then', | ||
'to', | ||
'trailing', | ||
'true', | ||
'union', | ||
'unique', | ||
'user', | ||
'using', | ||
'verbose', | ||
'when', | ||
'where', | ||
'with' | ||
]); | ||
//# sourceMappingURL=pg.js.map |
import { Nullable } from '../../util/mod'; | ||
import { SelectEntry } from "../sql-specs"; | ||
export interface SqlDialect { | ||
getRowObjectExpression(selectEntries: SelectEntry[], srcAlias: string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], orderBy: Nullable<string>, srcAlias: string): string; | ||
getAggregatedColumnValuesExpression(selectEntry: SelectEntry, orderBy: Nullable<string>, srcAlias: string): string; | ||
getRowObjectExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string): string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, orderBy: Nullable<string>): string; | ||
getAggregatedColumnValuesExpression(valueExpression: string, orderBy: Nullable<string>): string; | ||
quoteObjectNameIfNeeded(name: string): string; | ||
@@ -8,0 +8,0 @@ quoteColumnNameIfNeeded(name: string): string; |
@@ -11,7 +11,3 @@ import { CaseSensitivity } from "../dbmd"; | ||
makeSql(spec: SqlSpec): string; | ||
private baseSql; | ||
private makeSelectEntriesSql; | ||
private jsonRowObjectsSql; | ||
private additionalPropertyColumnSql; | ||
private aggregateSql; | ||
private selectEntriesSql; | ||
private selectEntrySql; | ||
@@ -23,3 +19,2 @@ private fromEntrySql; | ||
private indent; | ||
private maybeQuoteColumn; | ||
} |
@@ -16,36 +16,28 @@ "use strict"; | ||
makeSql(spec) { | ||
var _a, _b; | ||
const baseSql = this.baseSql(spec); | ||
const wrapPropsInObj = (_a = spec.objectWrapProperties) !== null && _a !== void 0 ? _a : false; | ||
if (!spec.aggregateToArray && !wrapPropsInObj) // no aggregation nor object wrapping | ||
{ | ||
return baseSql; | ||
} | ||
else // at least one of aggregation and object wrapping of properties will be done | ||
{ | ||
const propSelectEntries = (0, sql_specs_1.getPropertySelectEntries)(spec); | ||
const baseTable = baseTableDescn(spec); // only used for comments | ||
if (!spec.aggregateToArray) // no aggregation but do object wrapping | ||
{ | ||
const additionalCols = (_b = spec.additionalObjectPropertyColumns) !== null && _b !== void 0 ? _b : []; | ||
return this.jsonRowObjectsSql(baseSql, propSelectEntries, additionalCols, spec.orderBy, baseTable); | ||
} | ||
else // aggregation and maybe object wrapping | ||
{ | ||
return this.aggregateSql(baseSql, propSelectEntries, wrapPropsInObj, spec.orderBy, baseTable); | ||
} | ||
} | ||
} | ||
baseSql(spec) { | ||
var _a, _b, _c; | ||
const propSelEntries = (0, sql_specs_1.getPropertySelectEntries)(spec); | ||
const objWrap = (_a = spec.objectWrapProperties) !== null && _a !== void 0 ? _a : false; | ||
const arrayAgg = (_b = spec.aggregateToArray) !== null && _b !== void 0 ? _b : false; | ||
const addSelEntries = (_c = spec.additionalOutputSelectEntries) !== null && _c !== void 0 ? _c : []; | ||
const selectComment = this.genComments && spec.selectEntriesLeadingComment ? | ||
`-- ${spec.selectEntriesLeadingComment}\n` : ''; | ||
const selectEntries = this.makeSelectEntriesSql(spec.selectEntries) + '\n'; | ||
const selEntryValFn = (se) => this.selectEntrySql(se, true); | ||
const selectEntries = arrayAgg ? | ||
(objWrap | ||
? this.sqlDialect.getAggregatedRowObjectsExpression(propSelEntries, selEntryValFn, spec.aggregateOrderBy) | ||
: this.sqlDialect.getAggregatedColumnValuesExpression(this.selectEntrySql(propSelEntries[0], true), spec.aggregateOrderBy)) + ' json\n' | ||
: objWrap ? | ||
this.sqlDialect.getRowObjectExpression(propSelEntries, selEntryValFn) + ' json' + | ||
((0, collections_1.nonEmpty)(addSelEntries) | ||
? `,\n${addSelEntries.map(c => this.selectEntrySql(c)).join(',\n')}` | ||
: '') + "\n" | ||
: this.selectEntriesSql(spec.selectEntries) + "\n"; | ||
const fromComment = this.genComments && spec.fromEntriesLeadingComment ? | ||
`-- ${spec.fromEntriesLeadingComment}\n` : ''; | ||
const fromEntries = spec.fromEntries.map(e => this.fromEntrySql(e)).join('\n') + '\n'; | ||
const whereClause = (0, collections_1.isNonEmpty)(spec.whereEntries) | ||
? `where (\n${this.indent(spec.whereEntries.map(e => this.whereEntrySql(e)).join(' and\n'))}\n)\n` | ||
const whereClause = (0, collections_1.nonEmpty)(spec.whereEntries) ? | ||
`where (\n${this.indent(spec.whereEntries.map(e => this.whereEntrySql(e)).join(' and\n'))}\n)\n` | ||
: ''; | ||
const orderByClause = spec.orderBy | ||
? 'order by ' + spec.orderBy.orderBy.replace(/\$\$/g, spec.orderBy.tableAlias) + '\n' | ||
const orderByClause = spec.orderBy ? | ||
'order by ' + spec.orderBy.orderBy.replace(/\$\$/g, spec.orderBy.tableAlias) + '\n' | ||
: ''; | ||
@@ -57,3 +49,3 @@ return ('select\n' + this.indent(selectComment + selectEntries) + | ||
} | ||
makeSelectEntriesSql(specSelectEntries) { | ||
selectEntriesSql(specSelectEntries) { | ||
// Assign any missing displayOrders based on select entry position. | ||
@@ -68,46 +60,29 @@ const selectEntries = specSelectEntries.map((entry, ix) => { | ||
const sortedSelectEntries = (0, collections_1.sorted)(selectEntries, (e1, e2) => { var _a, _b; return ((_a = e1.displayOrder) !== null && _a !== void 0 ? _a : 0) - ((_b = e2.displayOrder) !== null && _b !== void 0 ? _b : 0); }); | ||
return sortedSelectEntries.map(e => this.selectEntrySql(e)).join(',\n'); | ||
return sortedSelectEntries.map(se => (this.genComments && se.comment ? `-- ${se.comment}\n` : '') + | ||
this.selectEntrySql(se)).join(',\n'); | ||
} | ||
jsonRowObjectsSql(baseSql, selectEntries, additionalColumns, orderBy, baseTableDesc) { | ||
return ('select\n' + | ||
this.indent((baseTableDesc ? `-- row object for table '${baseTableDesc}'\n` : '') + | ||
this.sqlDialect.getRowObjectExpression(selectEntries, 'q') + ' json' + | ||
((0, collections_1.isNonEmpty)(additionalColumns) | ||
? `,\n${additionalColumns.map(c => this.additionalPropertyColumnSql(c)).join(',\n')}` | ||
: '')) + '\n' + | ||
'from (\n' + | ||
nlterm(this.indent((baseTableDesc ? `-- base query for table '${baseTableDesc}'\n` : '') + | ||
baseSql)) + | ||
') q' + | ||
(orderBy != null ? '\norder by ' + orderBy.orderBy.replace(/\$\$/g, 'q') : '')); | ||
} | ||
additionalPropertyColumnSql(c) { | ||
if (typeof (c) === 'string') | ||
return this.sqlDialect.quoteColumnNameIfNeeded(c); | ||
const propNameExpr = this.sqlDialect.quoteColumnNameIfNeeded(c.property); | ||
const alias = this.sqlDialect.quoteColumnNameIfNeeded(c.as); | ||
return `${propNameExpr} as ${alias}`; | ||
} | ||
aggregateSql(sql, selectEntries, wrapProps, orderBy, baseTableDesc) { | ||
const ordby = orderBy === null || orderBy === void 0 ? void 0 : orderBy.orderBy; | ||
return ('select\n' + | ||
this.indent((this.genComments ? `-- aggregated ${wrapProps ? 'rows' : 'values'} from table '${baseTableDesc}'\n` : '') + | ||
(wrapProps | ||
? this.sqlDialect.getAggregatedRowObjectsExpression(selectEntries, ordby, 'q') | ||
: this.sqlDialect.getAggregatedColumnValuesExpression(selectEntries[0], ordby, 'q')) + ' json\n') + | ||
'from (\n' + | ||
nlterm(this.indent((this.genComments ? `-- base query for table '${baseTableDesc}'\n` : '') + | ||
sql)) + | ||
') q'); | ||
} | ||
selectEntrySql(selectEntry) { | ||
const projectedName = this.maybeQuoteColumn(selectEntry.projectedName); | ||
const isProjectedNameQuoted = projectedName !== selectEntry.projectedName; | ||
selectEntrySql(selectEntry, valueOnly = false) { | ||
switch (selectEntry.entryType) { | ||
case 'se-field': | ||
{ | ||
const fieldName = this.maybeQuoteColumn(selectEntry.field.name); | ||
const fieldName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.field.name); | ||
const valueSql = `${selectEntry.tableAlias}.${fieldName}`; | ||
if (valueOnly) | ||
return valueSql; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
const isProjectedNameQuoted = projectedName !== selectEntry.projectedName; | ||
const sep = isProjectedNameQuoted ? ' ' : ' as '; | ||
return `${selectEntry.tableAlias}.${fieldName}${sep}${projectedName}`; | ||
return `${valueSql}${sep}${projectedName}`; | ||
} | ||
case 'se-hidden-pkf': | ||
{ | ||
const fieldName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.pkFieldName); | ||
const valueSql = `${selectEntry.tableAlias}.${fieldName}`; | ||
if (valueOnly) | ||
return valueSql; | ||
const isFieldNameQuoted = fieldName !== selectEntry.pkFieldName; | ||
const sep = isFieldNameQuoted ? ' ' : ' as '; | ||
const exportedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
return `${valueSql}${sep}${exportedName}`; | ||
} | ||
case 'se-expr': | ||
@@ -117,2 +92,6 @@ { | ||
const expr = (0, strings_1.replaceAll)(selectEntry.expression, tableAliasPlaceholder, selectEntry.tableAlias); | ||
if (valueOnly) | ||
return expr; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
const isProjectedNameQuoted = projectedName !== selectEntry.projectedName; | ||
const sep = isProjectedNameQuoted ? ' ' : ' as '; | ||
@@ -123,4 +102,4 @@ return `${expr}${sep}${projectedName}`; | ||
{ | ||
return (this.genComments && selectEntry.comment ? `-- ${selectEntry.comment}\n` : '') + | ||
`${selectEntry.parentAlias}.${projectedName}`; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
return `${selectEntry.parentAlias}.${projectedName}`; | ||
} | ||
@@ -130,6 +109,7 @@ case 'se-parent-ref': | ||
const parentRowObjSql = this.makeSql(selectEntry.parentRowObjectSql); | ||
return (this.genComments && selectEntry.comment ? `-- ${selectEntry.comment}\n` : '') + | ||
'(\n' + | ||
this.indent(parentRowObjSql) + '\n' + | ||
`) ${projectedName}`; | ||
const valueSql = '(\n' + this.indent(parentRowObjSql) + ')'; | ||
if (valueOnly) | ||
return valueSql; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
return `${valueSql} ${projectedName}`; | ||
} | ||
@@ -139,15 +119,8 @@ case 'se-child-coll': | ||
const collectionSql = this.makeSql(selectEntry.collectionSql); | ||
return (this.genComments && selectEntry.comment ? `-- ${selectEntry.comment}\n` : '') + | ||
'(\n' + | ||
this.indent(collectionSql) + '\n' + | ||
`) ${projectedName}`; | ||
const valueSql = '(\n' + this.indent(collectionSql) + ')'; | ||
if (valueOnly) | ||
return valueSql; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
return `${valueSql} ${projectedName}`; | ||
} | ||
case 'se-hidden-pkf': | ||
{ | ||
const fieldName = this.maybeQuoteColumn(selectEntry.pkFieldName); | ||
const isFieldNameQuoted = fieldName !== selectEntry.pkFieldName; | ||
const sep = isFieldNameQuoted ? ' ' : ' as '; | ||
const exportedName = this.maybeQuoteColumn(selectEntry.projectedName); | ||
return `${selectEntry.tableAlias}.${fieldName}${sep}${exportedName}`; | ||
} | ||
} | ||
@@ -190,5 +163,5 @@ } | ||
: [pcCond.parentAlias, pcCond.fromAlias]; | ||
return pcCond.matchedFields.map(mf => `${childAlias}.${this.maybeQuoteColumn(mf.foreignKeyFieldName)}` + | ||
return pcCond.matchedFields.map(mf => `${childAlias}.${this.sqlDialect.quoteColumnNameIfNeeded(mf.foreignKeyFieldName)}` + | ||
' = ' + | ||
`${parentAlias}.${this.maybeQuoteColumn(mf.primaryKeyFieldName)}`).join(' and '); | ||
`${parentAlias}.${this.sqlDialect.quoteColumnNameIfNeeded(mf.primaryKeyFieldName)}`).join(' and '); | ||
} | ||
@@ -204,18 +177,5 @@ minimalRelIdentifier(relId) { | ||
} | ||
maybeQuoteColumn(colName) { | ||
return this.sqlDialect.quoteColumnNameIfNeeded(colName); | ||
} | ||
} | ||
exports.SqlSourceGenerator = SqlSourceGenerator; | ||
function baseTableDescn(sqlSpec) { | ||
const firstFromEntry = sqlSpec.fromEntries[0]; | ||
if (firstFromEntry.entryType === 'table') | ||
return firstFromEntry.table.name; | ||
return null; | ||
} | ||
// Newline terminate the given string if it doesn't end with a newline. | ||
function nlterm(s) { | ||
return !s.endsWith('\n') ? s + '\n' : s; | ||
} | ||
const DEFAULT_ALIAS_PLACEHOLDER = '$$'; | ||
//# sourceMappingURL=sql-source-generator.js.map |
@@ -32,4 +32,4 @@ "use strict"; | ||
case 'JSON_OBJECT_ROWS': | ||
const additionalCols = (_a = query.additionalObjectPropertyColumns) !== null && _a !== void 0 ? _a : []; | ||
return this.jsonObjectRowsSql(query.tableJson, additionalCols, null, query.orderBy, specLoc); | ||
const addCols = (_a = query.additionalOutputColumns) !== null && _a !== void 0 ? _a : []; | ||
return this.jsonObjectRowsSql(query.tableJson, addCols, null, query.orderBy, specLoc); | ||
case 'JSON_ARRAY_ROW': | ||
@@ -53,3 +53,3 @@ return this.jsonArrayRowSql(query.tableJson, null, false, query.orderBy, specLoc); | ||
sqlb.addSelectEntries(this.hiddenPrimaryKeySelectEntries(relId, alias)); | ||
sqlb.addSelectEntries(this.tableFieldExpressionSelectEntries(tjs, alias, specLoc)); | ||
sqlb.addSelectEntries(this.tableFieldExpressionSelectEntries(tjs.fieldExpressions, tjs.table, alias, specLoc)); | ||
sqlb.addParts(this.inlineParentsSqlParts(tjs, relId, alias, sqlb.getAliases(), specLoc)); | ||
@@ -77,17 +77,10 @@ sqlb.addParts(this.referencedParentsSqlParts(tjs, relId, alias, specLoc)); | ||
/// Make query SQL having JSON object result values at the top level of the | ||
/// result set. The query returns a JSON value in a single column and with | ||
/// any number of result rows. | ||
jsonObjectRowsSql(tjs, additionalObjectPropertyColumns, pkCond, orderBy, specLoc) { | ||
/// result set. The query returns a JSON value in a single column for each | ||
/// of any number of result rows. | ||
jsonObjectRowsSql(tjs, addOutputColumns, pkCond, orderBy, specLoc) { | ||
const baseSql = this.baseSql(tjs, pkCond, orderBy, specLoc); | ||
// Additional property column names must exist as projected column names in the base sql. | ||
const propCols = additionalObjectPropertyColumns.map(c => typeof (c) == 'string' ? c : c.property); | ||
const projectedPropNames = new Set((0, sql_specs_1.getPropertySelectEntries)(baseSql).map(prop => prop.projectedName)); | ||
const invalidPropCols = propCols.filter(pc => !projectedPropNames.has(pc)); | ||
if (invalidPropCols.length > 0) | ||
throw new query_specs_1.SpecError(specLoc, `Extracted property column(s) not found: ${invalidPropCols.join(', ')}.`); | ||
return { | ||
...baseSql, | ||
objectWrapProperties: true, | ||
additionalObjectPropertyColumns | ||
}; | ||
(0, query_specs_1.verifyTableFieldExpressionsValid)(addOutputColumns, tjs.table, this.defaultSchema, this.dbmd, specLoc); | ||
const alias = baseSql.fromEntries[0].alias; | ||
const additionalOutputSelectEntries = this.tableFieldExpressionSelectEntries(addOutputColumns, tjs.table, alias, specLoc); | ||
return { ...baseSql, objectWrapProperties: true, additionalOutputSelectEntries }; | ||
} | ||
@@ -98,3 +91,3 @@ /// Make query SQL having a single row and column result, with the result value | ||
jsonArrayRowSql(tjs, childFkCond, unwrap, orderBy, specLoc) { | ||
const baseSql = this.baseSql(tjs, childFkCond, orderBy, specLoc); | ||
const baseSql = this.baseSql(tjs, childFkCond, null, specLoc); | ||
if (unwrap && (0, sql_specs_1.getPropertySelectEntries)(baseSql).length != 1) | ||
@@ -105,4 +98,4 @@ throw new query_specs_1.SpecError(specLoc, 'Unwrapped collections cannot have multiple properties.'); | ||
aggregateToArray: true, | ||
objectWrapProperties: !unwrap, | ||
fromEntriesLeadingComment: `base query for table '${tjs.table}'`, | ||
aggregateOrderBy: orderBy, | ||
objectWrapProperties: !unwrap | ||
}; | ||
@@ -118,9 +111,9 @@ } | ||
} | ||
tableFieldExpressionSelectEntries(tjs, tableAlias, specLoc) { | ||
(0, query_specs_1.verifyTableFieldExpressionsValid)(tjs, this.defaultSchema, this.dbmd, specLoc); | ||
if (!tjs.fieldExpressions) | ||
tableFieldExpressionSelectEntries(fieldExpressions, table, tableAlias, specLoc) { | ||
(0, query_specs_1.verifyTableFieldExpressionsValid)(fieldExpressions, table, this.defaultSchema, this.dbmd, specLoc); | ||
if (!fieldExpressions) | ||
return []; | ||
const dbFieldsByName = this.getTableFieldsByName(tjs.table, specLoc); | ||
return tjs.fieldExpressions.map((tfe, ix) => { | ||
const feLoc = (0, query_specs_1.addLocPart)(specLoc, `fieldExpressions entry #${ix + 1} of table ${tjs.table}`); | ||
const dbFieldsByName = this.getTableFieldsByName(table, specLoc); | ||
return fieldExpressions.map((tfe, ix) => { | ||
const feLoc = (0, query_specs_1.addLocPart)(specLoc, `fieldExpressions entry #${ix + 1} of table ${table}`); | ||
const projectedName = this.jsonPropertyName(tfe, feLoc); | ||
@@ -131,3 +124,3 @@ if (typeof tfe === 'string' || tfe.field != null) { | ||
if (dbField == undefined) | ||
throw new query_specs_1.SpecError(specLoc, `No metadata found for field ${tjs.table}.${fieldName}.`); | ||
throw new query_specs_1.SpecError(specLoc, `No metadata found for field ${table}.${fieldName}.`); | ||
return { | ||
@@ -160,14 +153,13 @@ entryType: 'se-field', | ||
const sqlParts = new sql_specs_1.SqlParts(aliasesInScope); | ||
for (const parent of (0, query_specs_1.getInlineParentSpecs)(tjs)) { | ||
const ipLoc = (0, query_specs_1.addLocPart)(specLoc, `parent table '${parent.table}'`); | ||
sqlParts.addParts(this.inlineParentSqlParts(parent, relId, alias, sqlParts.getAliases(), ipLoc)); | ||
for (const parSpec of (0, query_specs_1.getInlineParentSpecs)(tjs)) { | ||
const ipLoc = (0, query_specs_1.addLocPart)(specLoc, `parent table '${parSpec.table}'`); | ||
sqlParts.addParts(this.inlineParentSqlParts(parSpec, relId, alias, sqlParts.getAliases(), ipLoc)); | ||
} | ||
return sqlParts; | ||
} | ||
// Return sql parts for the *including* (child) table's SQL query which are contributed by the inline parent table. | ||
inlineParentSqlParts(parentSpec, childRelId, childAlias, avoidAliases, specLoc) { | ||
var _a, _b; | ||
const sqlParts = new sql_specs_1.SqlParts(); | ||
const subqueryAlias = parentSpec.subqueryAlias || (0, mod_1.makeNameNotInSet)('q', avoidAliases); | ||
sqlParts.addAlias(subqueryAlias); | ||
const fromEntryAlias = parentSpec.fromEntryAlias || (0, sql_specs_1.createTableAlias)(parentSpec.table, avoidAliases); | ||
sqlParts.addAlias(fromEntryAlias); | ||
const parentPropsSql = this.baseSql(parentSpec, null, null, specLoc, 'export-pk-fields-hidden'); | ||
@@ -183,3 +175,3 @@ const matchedFields = this.getParentPrimaryKeyCondition(parentSpec, childRelId, childAlias, specLoc) | ||
query: parentPropsSql, | ||
alias: subqueryAlias, | ||
alias: fromEntryAlias, | ||
join: { | ||
@@ -190,3 +182,3 @@ joinType: 'LEFT', | ||
fromAlias: childAlias, | ||
parentAlias: subqueryAlias, | ||
parentAlias: fromEntryAlias, | ||
matchedFields, | ||
@@ -203,3 +195,3 @@ matchMustExist | ||
projectedName: parentSelectEntry.projectedName, | ||
parentAlias: subqueryAlias, | ||
parentAlias: fromEntryAlias, | ||
parentTable: parentRelId, | ||
@@ -206,0 +198,0 @@ parentSelectEntry: parentSelectEntry, |
import { Field, ForeignKeyComponent, RelId } from "../dbmd"; | ||
import { AdditionalObjectPropertyColumn } from "../query-specs"; | ||
import { Nullable } from "../util/mod"; | ||
@@ -11,4 +10,5 @@ export interface SqlSpec { | ||
objectWrapProperties?: Nullable<boolean>; | ||
additionalObjectPropertyColumns?: Nullable<AdditionalObjectPropertyColumn[]>; | ||
additionalOutputSelectEntries?: Nullable<AdditionalOutputSelectEntry[]>; | ||
aggregateToArray?: Nullable<boolean>; | ||
aggregateOrderBy?: Nullable<string>; | ||
selectEntriesLeadingComment?: Nullable<string>; | ||
@@ -28,2 +28,3 @@ fromEntriesLeadingComment?: Nullable<string>; | ||
}>; | ||
readonly comment?: Nullable<string>; | ||
} | ||
@@ -40,2 +41,3 @@ export interface ExpressionSelectEntry { | ||
}; | ||
readonly comment?: Nullable<string>; | ||
} | ||
@@ -71,3 +73,5 @@ export interface InlineParentSelectEntry { | ||
readonly displayOrder?: undefined; | ||
readonly comment?: Nullable<string>; | ||
} | ||
export type AdditionalOutputSelectEntry = FieldSelectEntry | ExpressionSelectEntry; | ||
export type FromEntry = TableFromEntry | QueryFromEntry; | ||
@@ -79,3 +83,3 @@ export interface TableFromEntry { | ||
readonly join?: Nullable<Join>; | ||
readonly comment?: undefined; | ||
readonly comment?: Nullable<string>; | ||
} | ||
@@ -143,1 +147,3 @@ export interface QueryFromEntry { | ||
export declare function getBaseTable(sql: SqlSpec): RelId; | ||
export declare const generalSqlKeywordsLowercase: Set<string>; | ||
export declare function createTableAlias(relName: string, avoid: Set<string>): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getBaseTable = exports.getPropertySelectEntries = exports.SqlParts = void 0; | ||
exports.createTableAlias = exports.generalSqlKeywordsLowercase = exports.getBaseTable = exports.getPropertySelectEntries = exports.SqlParts = void 0; | ||
const collections_1 = require("../util/collections"); | ||
@@ -52,3 +52,3 @@ const strings_1 = require("../util/strings"); | ||
createTableAlias(relName) { | ||
const alias = (0, strings_1.makeNameNotInSet)((0, strings_1.lowerCaseInitials)(relName, '_') + '_', this.aliases); | ||
const alias = createTableAlias(relName, this.aliases); | ||
this.aliases.add(alias); | ||
@@ -90,2 +90,837 @@ return alias; | ||
exports.getBaseTable = getBaseTable; | ||
exports.generalSqlKeywordsLowercase = new Set([ | ||
'a', | ||
'abort', | ||
'abs', | ||
'absolute', | ||
'access', | ||
'action', | ||
'ada', | ||
'add', | ||
'admin', | ||
'after', | ||
'aggregate', | ||
'alias', | ||
'all', | ||
'allocate', | ||
'also', | ||
'alter', | ||
'always', | ||
'analyse', | ||
'analyze', | ||
'and', | ||
'any', | ||
'are', | ||
'array', | ||
'as', | ||
'asc', | ||
'asensitive', | ||
'assertion', | ||
'assignment', | ||
'asymmetric', | ||
'at', | ||
'atomic', | ||
'attribute', | ||
'attributes', | ||
'audit', | ||
'authorization', | ||
'auto_increment', | ||
'avg', | ||
'avg_row_length', | ||
'backup', | ||
'backward', | ||
'before', | ||
'begin', | ||
'bernoulli', | ||
'between', | ||
'bigint', | ||
'binary', | ||
'bit', | ||
'bit_length', | ||
'bitvar', | ||
'blob', | ||
'bool', | ||
'boolean', | ||
'both', | ||
'breadth', | ||
'break', | ||
'browse', | ||
'bulk', | ||
'by', | ||
'c', | ||
'cache', | ||
'call', | ||
'called', | ||
'cardinality', | ||
'cascade', | ||
'cascaded', | ||
'case', | ||
'cast', | ||
'catalog', | ||
'catalog_name', | ||
'ceil', | ||
'ceiling', | ||
'chain', | ||
'change', | ||
'char', | ||
'char_length', | ||
'character', | ||
'character_length', | ||
'character_set_catalog', | ||
'character_set_name', | ||
'character_set_schema', | ||
'characteristics', | ||
'characters', | ||
'check', | ||
'checked', | ||
'checkpoint', | ||
'checksum', | ||
'class', | ||
'class_origin', | ||
'clob', | ||
'close', | ||
'cluster', | ||
'clustered', | ||
'coalesce', | ||
'cobol', | ||
'collate', | ||
'collation', | ||
'collation_catalog', | ||
'collation_name', | ||
'collation_schema', | ||
'collect', | ||
'column', | ||
'column_name', | ||
'columns', | ||
'command_function', | ||
'command_function_code', | ||
'comment', | ||
'commit', | ||
'committed', | ||
'completion', | ||
'compress', | ||
'compute', | ||
'condition', | ||
'condition_number', | ||
'connect', | ||
'connection', | ||
'connection_name', | ||
'constraint', | ||
'constraint_catalog', | ||
'constraint_name', | ||
'constraint_schema', | ||
'constraints', | ||
'constructor', | ||
'contains', | ||
'containstable', | ||
'continue', | ||
'conversion', | ||
'convert', | ||
'copy', | ||
'corr', | ||
'corresponding', | ||
'count', | ||
'covar_pop', | ||
'covar_samp', | ||
'create', | ||
'createdb', | ||
'createrole', | ||
'createuser', | ||
'cross', | ||
'csv', | ||
'cube', | ||
'cume_dist', | ||
'current', | ||
'current_date', | ||
'current_default_transform_group', | ||
'current_path', | ||
'current_role', | ||
'current_time', | ||
'current_timestamp', | ||
'current_transform_group_for_type', | ||
'current_user', | ||
'cursor', | ||
'cursor_name', | ||
'cycle', | ||
'data', | ||
'database', | ||
'databases', | ||
'date', | ||
'datetime', | ||
'datetime_interval_code', | ||
'datetime_interval_precision', | ||
'day', | ||
'day_hour', | ||
'day_microsecond', | ||
'day_minute', | ||
'day_second', | ||
'dayofmonth', | ||
'dayofweek', | ||
'dayofyear', | ||
'dbcc', | ||
'deallocate', | ||
'dec', | ||
'decimal', | ||
'declare', | ||
'default', | ||
'defaults', | ||
'deferrable', | ||
'deferred', | ||
'defined', | ||
'definer', | ||
'degree', | ||
'delay_key_write', | ||
'delayed', | ||
'delete', | ||
'delimiter', | ||
'delimiters', | ||
'dense_rank', | ||
'deny', | ||
'depth', | ||
'deref', | ||
'derived', | ||
'desc', | ||
'describe', | ||
'descriptor', | ||
'destroy', | ||
'destructor', | ||
'deterministic', | ||
'diagnostics', | ||
'dictionary', | ||
'disable', | ||
'disconnect', | ||
'disk', | ||
'dispatch', | ||
'distinct', | ||
'distinctrow', | ||
'distributed', | ||
'div', | ||
'do', | ||
'domain', | ||
'double', | ||
'drop', | ||
'dual', | ||
'dummy', | ||
'dump', | ||
'dynamic', | ||
'dynamic_function', | ||
'dynamic_function_code', | ||
'each', | ||
'element', | ||
'else', | ||
'elseif', | ||
'enable', | ||
'enclosed', | ||
'encoding', | ||
'encrypted', | ||
'end', | ||
'end-exec', | ||
'enum', | ||
'equals', | ||
'errlvl', | ||
'escape', | ||
'escaped', | ||
'every', | ||
'except', | ||
'exception', | ||
'exclude', | ||
'excluding', | ||
'exclusive', | ||
'exec', | ||
'execute', | ||
'existing', | ||
'exists', | ||
'exit', | ||
'exp', | ||
'explain', | ||
'external', | ||
'extract', | ||
'false', | ||
'fetch', | ||
'fields', | ||
'file', | ||
'fillfactor', | ||
'filter', | ||
'final', | ||
'first', | ||
'float', | ||
'float4', | ||
'float8', | ||
'floor', | ||
'flush', | ||
'following', | ||
'for', | ||
'force', | ||
'foreign', | ||
'fortran', | ||
'forward', | ||
'found', | ||
'free', | ||
'freetext', | ||
'freetexttable', | ||
'freeze', | ||
'from', | ||
'full', | ||
'fulltext', | ||
'function', | ||
'fusion', | ||
'g', | ||
'general', | ||
'generated', | ||
'get', | ||
'global', | ||
'go', | ||
'goto', | ||
'grant', | ||
'granted', | ||
'grants', | ||
'greatest', | ||
'group', | ||
'grouping', | ||
'handler', | ||
'having', | ||
'header', | ||
'heap', | ||
'hierarchy', | ||
'high_priority', | ||
'hold', | ||
'holdlock', | ||
'host', | ||
'hosts', | ||
'hour', | ||
'hour_microsecond', | ||
'hour_minute', | ||
'hour_second', | ||
'identified', | ||
'identity', | ||
'identity_insert', | ||
'identitycol', | ||
'if', | ||
'ignore', | ||
'ilike', | ||
'immediate', | ||
'immutable', | ||
'implementation', | ||
'implicit', | ||
'in', | ||
'include', | ||
'including', | ||
'increment', | ||
'index', | ||
'indicator', | ||
'infile', | ||
'infix', | ||
'inherit', | ||
'inherits', | ||
'initial', | ||
'initialize', | ||
'initially', | ||
'inner', | ||
'inout', | ||
'input', | ||
'insensitive', | ||
'insert', | ||
'insert_id', | ||
'instance', | ||
'instantiable', | ||
'instead', | ||
'int', | ||
'int1', | ||
'int2', | ||
'int3', | ||
'int4', | ||
'int8', | ||
'integer', | ||
'intersect', | ||
'intersection', | ||
'interval', | ||
'into', | ||
'invoker', | ||
'is', | ||
'isam', | ||
'isnull', | ||
'isolation', | ||
'iterate', | ||
'join', | ||
'k', | ||
'key', | ||
'key_member', | ||
'key_type', | ||
'keys', | ||
'kill', | ||
'lancompiler', | ||
'language', | ||
'large', | ||
'last', | ||
'last_insert_id', | ||
'lateral', | ||
'lead', | ||
'leading', | ||
'least', | ||
'leave', | ||
'left', | ||
'length', | ||
'less', | ||
'level', | ||
'like', | ||
'limit', | ||
'lineno', | ||
'lines', | ||
'listen', | ||
'ln', | ||
'load', | ||
'local', | ||
'localtime', | ||
'localtimestamp', | ||
'location', | ||
'locator', | ||
'lock', | ||
'login', | ||
'logs', | ||
'long', | ||
'longblob', | ||
'longtext', | ||
'loop', | ||
'low_priority', | ||
'lower', | ||
'm', | ||
'map', | ||
'match', | ||
'matched', | ||
'max', | ||
'max_rows', | ||
'maxextents', | ||
'maxvalue', | ||
'mediumblob', | ||
'mediumint', | ||
'mediumtext', | ||
'member', | ||
'merge', | ||
'message_length', | ||
'message_octet_length', | ||
'message_text', | ||
'method', | ||
'middleint', | ||
'min', | ||
'min_rows', | ||
'minus', | ||
'minute', | ||
'minute_microsecond', | ||
'minute_second', | ||
'minvalue', | ||
'mlslabel', | ||
'mod', | ||
'mode', | ||
'modifies', | ||
'modify', | ||
'module', | ||
'month', | ||
'monthname', | ||
'more', | ||
'move', | ||
'multiset', | ||
'mumps', | ||
'myisam', | ||
'name', | ||
'names', | ||
'national', | ||
'natural', | ||
'nchar', | ||
'nclob', | ||
'nesting', | ||
'new', | ||
'next', | ||
'no', | ||
'no_write_to_binlog', | ||
'noaudit', | ||
'nocheck', | ||
'nocompress', | ||
'nocreatedb', | ||
'nocreaterole', | ||
'nocreateuser', | ||
'noinherit', | ||
'nologin', | ||
'nonclustered', | ||
'none', | ||
'normalize', | ||
'normalized', | ||
'nosuperuser', | ||
'not', | ||
'nothing', | ||
'notify', | ||
'notnull', | ||
'nowait', | ||
'null', | ||
'nullable', | ||
'nullif', | ||
'nulls', | ||
'number', | ||
'numeric', | ||
'object', | ||
'octet_length', | ||
'octets', | ||
'of', | ||
'off', | ||
'offline', | ||
'offset', | ||
'offsets', | ||
'oids', | ||
'old', | ||
'on', | ||
'online', | ||
'only', | ||
'open', | ||
'opendatasource', | ||
'openquery', | ||
'openrowset', | ||
'openxml', | ||
'operation', | ||
'operator', | ||
'optimize', | ||
'option', | ||
'optionally', | ||
'options', | ||
'or', | ||
'order', | ||
'ordering', | ||
'ordinality', | ||
'others', | ||
'out', | ||
'outer', | ||
'outfile', | ||
'output', | ||
'over', | ||
'overlaps', | ||
'overlay', | ||
'overriding', | ||
'owner', | ||
'pack_keys', | ||
'pad', | ||
'parameter', | ||
'parameter_mode', | ||
'parameter_name', | ||
'parameter_ordinal_position', | ||
'parameter_specific_catalog', | ||
'parameter_specific_name', | ||
'parameter_specific_schema', | ||
'parameters', | ||
'partial', | ||
'partition', | ||
'pascal', | ||
'password', | ||
'path', | ||
'pctfree', | ||
'percent', | ||
'percent_rank', | ||
'percentile_cont', | ||
'percentile_disc', | ||
'placing', | ||
'plan', | ||
'pli', | ||
'position', | ||
'postfix', | ||
'power', | ||
'preceding', | ||
'precision', | ||
'prefix', | ||
'preorder', | ||
'prepare', | ||
'prepared', | ||
'preserve', | ||
'primary', | ||
'print', | ||
'prior', | ||
'privileges', | ||
'proc', | ||
'procedural', | ||
'procedure', | ||
'process', | ||
'processlist', | ||
'public', | ||
'purge', | ||
'quote', | ||
'raid0', | ||
'raiserror', | ||
'range', | ||
'rank', | ||
'raw', | ||
'read', | ||
'reads', | ||
'readtext', | ||
'real', | ||
'recheck', | ||
'reconfigure', | ||
'recursive', | ||
'ref', | ||
'references', | ||
'referencing', | ||
'regexp', | ||
'regr_avgx', | ||
'regr_avgy', | ||
'regr_count', | ||
'regr_intercept', | ||
'regr_r2', | ||
'regr_slope', | ||
'regr_sxx', | ||
'regr_sxy', | ||
'regr_syy', | ||
'reindex', | ||
'relative', | ||
'release', | ||
'reload', | ||
'rename', | ||
'repeat', | ||
'repeatable', | ||
'replace', | ||
'replication', | ||
'require', | ||
'reset', | ||
'resignal', | ||
'resource', | ||
'restart', | ||
'restore', | ||
'restrict', | ||
'result', | ||
'return', | ||
'returned_cardinality', | ||
'returned_length', | ||
'returned_octet_length', | ||
'returned_sqlstate', | ||
'returns', | ||
'revoke', | ||
'right', | ||
'rlike', | ||
'role', | ||
'rollback', | ||
'rollup', | ||
'routine', | ||
'routine_catalog', | ||
'routine_name', | ||
'routine_schema', | ||
'row', | ||
'row_count', | ||
'row_number', | ||
'rowcount', | ||
'rowguidcol', | ||
'rowid', | ||
'rownum', | ||
'rows', | ||
'rule', | ||
'save', | ||
'savepoint', | ||
'scale', | ||
'schema', | ||
'schema_name', | ||
'schemas', | ||
'scope', | ||
'scope_catalog', | ||
'scope_name', | ||
'scope_schema', | ||
'scroll', | ||
'search', | ||
'second', | ||
'second_microsecond', | ||
'section', | ||
'security', | ||
'select', | ||
'self', | ||
'sensitive', | ||
'separator', | ||
'sequence', | ||
'serializable', | ||
'server_name', | ||
'session', | ||
'session_user', | ||
'set', | ||
'setof', | ||
'sets', | ||
'setuser', | ||
'share', | ||
'show', | ||
'shutdown', | ||
'signal', | ||
'similar', | ||
'simple', | ||
'size', | ||
'smallint', | ||
'some', | ||
'soname', | ||
'source', | ||
'space', | ||
'spatial', | ||
'specific', | ||
'specific_name', | ||
'specifictype', | ||
'sql', | ||
'sql_big_result', | ||
'sql_big_selects', | ||
'sql_big_tables', | ||
'sql_calc_found_rows', | ||
'sql_log_off', | ||
'sql_log_update', | ||
'sql_low_priority_updates', | ||
'sql_select_limit', | ||
'sql_small_result', | ||
'sql_warnings', | ||
'sqlca', | ||
'sqlcode', | ||
'sqlerror', | ||
'sqlexception', | ||
'sqlstate', | ||
'sqlwarning', | ||
'sqrt', | ||
'ssl', | ||
'stable', | ||
'start', | ||
'starting', | ||
'state', | ||
'statement', | ||
'static', | ||
'statistics', | ||
'status', | ||
'stddev_pop', | ||
'stddev_samp', | ||
'stdin', | ||
'stdout', | ||
'storage', | ||
'straight_join', | ||
'strict', | ||
'string', | ||
'structure', | ||
'style', | ||
'subclass_origin', | ||
'sublist', | ||
'submultiset', | ||
'substring', | ||
'successful', | ||
'sum', | ||
'superuser', | ||
'symmetric', | ||
'synonym', | ||
'sysdate', | ||
'sysid', | ||
'system', | ||
'system_user', | ||
'table', | ||
'table_name', | ||
'tables', | ||
'tablesample', | ||
'tablespace', | ||
'temp', | ||
'template', | ||
'temporary', | ||
'terminate', | ||
'terminated', | ||
'text', | ||
'textsize', | ||
'than', | ||
'then', | ||
'ties', | ||
'time', | ||
'timestamp', | ||
'timezone_hour', | ||
'timezone_minute', | ||
'tinyblob', | ||
'tinyint', | ||
'tinytext', | ||
'to', | ||
'toast', | ||
'top', | ||
'top_level_count', | ||
'trailing', | ||
'tran', | ||
'transaction', | ||
'transaction_active', | ||
'transactions_committed', | ||
'transactions_rolled_back', | ||
'transform', | ||
'transforms', | ||
'translate', | ||
'translation', | ||
'treat', | ||
'trigger', | ||
'trigger_catalog', | ||
'trigger_name', | ||
'trigger_schema', | ||
'trim', | ||
'true', | ||
'truncate', | ||
'trusted', | ||
'tsequal', | ||
'type', | ||
'uescape', | ||
'uid', | ||
'unbounded', | ||
'uncommitted', | ||
'under', | ||
'undo', | ||
'unencrypted', | ||
'union', | ||
'unique', | ||
'unknown', | ||
'unlisten', | ||
'unlock', | ||
'unnamed', | ||
'unnest', | ||
'unsigned', | ||
'until', | ||
'update', | ||
'updatetext', | ||
'upper', | ||
'usage', | ||
'use', | ||
'user', | ||
'user_defined_type_catalog', | ||
'user_defined_type_code', | ||
'user_defined_type_name', | ||
'user_defined_type_schema', | ||
'using', | ||
'utc_date', | ||
'utc_time', | ||
'utc_timestamp', | ||
'vacuum', | ||
'valid', | ||
'validate', | ||
'validator', | ||
'value', | ||
'values', | ||
'var_pop', | ||
'var_samp', | ||
'varbinary', | ||
'varchar', | ||
'varchar2', | ||
'varcharacter', | ||
'variable', | ||
'variables', | ||
'varying', | ||
'verbose', | ||
'view', | ||
'volatile', | ||
'waitfor', | ||
'when', | ||
'whenever', | ||
'where', | ||
'while', | ||
'width_bucket', | ||
'window', | ||
'with', | ||
'within', | ||
'without', | ||
'work', | ||
'write', | ||
'writetext', | ||
'x509', | ||
'xor', | ||
'year', | ||
'year_month', | ||
'zerofill', | ||
'zone', | ||
]); | ||
function createTableAlias(relName, avoid) { | ||
let alias = (0, strings_1.makeNameNotInSet)((0, strings_1.lowerCaseInitials)(relName, '_'), avoid); | ||
if (alias.length > 1 && exports.generalSqlKeywordsLowercase.has(alias.toLowerCase())) | ||
alias = alias + "_"; | ||
return alias; | ||
} | ||
exports.createTableAlias = createTableAlias; | ||
//# sourceMappingURL=sql-specs.js.map |
@@ -12,2 +12,2 @@ export declare function computeIfAbsent<K, V>(m: Map<K, V>, k: K, valueMaker: (k: K) => V): V; | ||
export declare function firstValue<K, V>(m: Map<K, V>): V; | ||
export declare function isNonEmpty<T>(ts: T[] | null | undefined): ts is NonNullable<T[]>; | ||
export declare function nonEmpty<T>(ts: T[] | null | undefined): ts is NonNullable<T[]>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isNonEmpty = exports.firstValue = exports.dedupe = exports.sorted = exports.partitionByEquality = exports.makeArrayValuesMap = exports.mapEntries = exports.mapValues = exports.makeMap = exports.mapSet = exports.setsEqual = exports.computeIfAbsent = void 0; | ||
exports.nonEmpty = exports.firstValue = exports.dedupe = exports.sorted = exports.partitionByEquality = exports.makeArrayValuesMap = exports.mapEntries = exports.mapValues = exports.makeMap = exports.mapSet = exports.setsEqual = exports.computeIfAbsent = void 0; | ||
function computeIfAbsent(m, k, valueMaker) { | ||
@@ -129,6 +129,6 @@ const maybeVal = m.get(k); | ||
} | ||
function isNonEmpty(ts) { | ||
function nonEmpty(ts) { | ||
return (ts != null && ts.length > 0); | ||
} | ||
exports.isNonEmpty = isNonEmpty; | ||
exports.nonEmpty = nonEmpty; | ||
//# sourceMappingURL=collections.js.map |
@@ -8,4 +8,5 @@ export declare function upperCamelCase(name: string): string; | ||
export declare function makeNameNotInSet(baseName: string, existingNames: Set<string>, suffixSep?: string): string; | ||
export declare function makeNameNotInSets(baseName: string, avoidSets: Set<string>[], suffixSep?: string): string; | ||
export declare function indentLines(linesStr: string, spacesCount: number, indentFirstLine?: boolean): string; | ||
export declare function unDoubleQuote(s: string): string; | ||
export declare function hashString(s: string): number; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.hashString = exports.unDoubleQuote = exports.indentLines = exports.makeNameNotInSet = exports.snakeCase = exports.replaceAll = exports.escapeRegExp = exports.lowerCaseInitials = exports.lowerCamelCase = exports.upperCamelCase = void 0; | ||
exports.hashString = exports.unDoubleQuote = exports.indentLines = exports.makeNameNotInSets = exports.makeNameNotInSet = exports.snakeCase = exports.replaceAll = exports.escapeRegExp = exports.lowerCaseInitials = exports.lowerCamelCase = exports.upperCamelCase = void 0; | ||
function upperCamelCase(name) { | ||
@@ -13,4 +13,6 @@ return camelCase(name, true); | ||
function lowerCaseInitials(name, sep) { | ||
const cleanedName = name.replaceAll(/[._ ]/g, '_') | ||
.replaceAll(/[^a-zA-Z0-9_]/g, ""); | ||
const parts = []; | ||
for (const word of name.split(sep)) { | ||
for (const word of cleanedName.split(sep)) { | ||
if (word.length > 0) | ||
@@ -65,2 +67,14 @@ parts.push(word.charAt(0).toLowerCase()); | ||
exports.makeNameNotInSet = makeNameNotInSet; | ||
function makeNameNotInSets(baseName, avoidSets, suffixSep = "") { | ||
const nameOk = (name) => !avoidSets.some(set => set.has(name)); | ||
if (nameOk(baseName)) | ||
return baseName; | ||
else { | ||
let i = 1; | ||
while (!nameOk(baseName + suffixSep + i)) | ||
++i; | ||
return baseName + suffixSep + i; | ||
} | ||
} | ||
exports.makeNameNotInSets = makeNameNotInSets; | ||
function indentLines(linesStr, spacesCount, indentFirstLine = true) { | ||
@@ -67,0 +81,0 @@ const sb = []; |
{ | ||
"name": "sqljson-query", | ||
"version": "1.9.1", | ||
"version": "1.9.2", | ||
"description": "Command line tool to generate SQL/JSON SQL queries and result types for Typescript or Java.", | ||
@@ -25,3 +25,3 @@ "keywords": [ | ||
"build-pg": "docker build -t drugs-pg src/__tests__/db/pg", | ||
"start-pg": "npm run build-pg && docker run -d --name drugs-pg --rm -p 127.0.0.1:5432:5432 --shm-size=256MB drugs-pg", | ||
"start-pg": "npm run build-pg && docker run -d --name drugs-pg --rm -p 5432:5432 --shm-size=256MB drugs-pg", | ||
"pg-cli": "docker exec -it drugs-pg psql -U drugs", | ||
@@ -41,9 +41,9 @@ "build-mysql": "docker build -t drugs-mysql src/__tests__/db/mysql", | ||
"ajv": "^8.11.2", | ||
"dotenv": "^16.0.3", | ||
"lodash": "^4.17.21", | ||
"minimist": "^1.2.7", | ||
"mysql2": "^2.3.3", | ||
"path": "^0.12.7", | ||
"pg": "^8.8.0", | ||
"zod": "^3.19.1", | ||
"dotenv": "^16.0.3", | ||
"path": "^0.12.7" | ||
"zod": "^3.19.1" | ||
}, | ||
@@ -54,5 +54,5 @@ "devDependencies": { | ||
"@types/lodash": "^4.14.191", | ||
"@types/minimist": "^1.2.2", | ||
"@types/node": "^18.11.10", | ||
"@types/pg": "^8.6.5", | ||
"@types/minimist": "^1.2.2", | ||
"copyfiles": "^2.4.1", | ||
@@ -59,0 +59,0 @@ "jest": "^29.3.1", |
import * as path from 'path'; | ||
import { promises as fs } from 'fs'; | ||
import { requireFileExists, writeTextFile, readTextFile } from './util/files'; | ||
import { parseArgs, parseBoolOption } from './util/args'; | ||
import { Nullable, replaceAll} from './util/mod'; | ||
import { QueryGroupSpec, ResultRepr, SpecError } from './query-specs'; | ||
import { SourceGenerationOptions, SourceLanguage } from './source-generation-options'; | ||
import { QueryPropertiesMetadata } from './query-properties-metadata-generation'; | ||
import {promises as fs} from 'fs'; | ||
import {readTextFile, requireFileExists, writeTextFile} from './util/files'; | ||
import {parseArgs, parseBoolOption} from './util/args'; | ||
import {Nullable, replaceAll} from './util/mod'; | ||
import {QueryGroupSpec, ResultRepr, SpecError} from './query-specs'; | ||
import {SourceGenerationOptions, SourceLanguage} from './source-generation-options'; | ||
import {QueryPropertiesMetadata} from './query-properties-metadata-generation'; | ||
import { | ||
DatabaseMetadata, GeneratedResultTypes, GeneratedSql, generateQueryGroupSources, | ||
DatabaseMetadata, | ||
GeneratedResultTypes, | ||
GeneratedSql, | ||
generateQueryGroupSources, | ||
parseStoredDatabaseMetadata | ||
@@ -12,0 +15,0 @@ } from './lib'; |
import { | ||
getBaseTable, getPropertySelectEntries, QueryFromEntry, SelectEntry, SqlSpec | ||
getBaseTable, | ||
getPropertySelectEntries, | ||
QueryFromEntry, | ||
SelectEntry, | ||
SqlSpec | ||
} from "../sql-generation/sql-specs"; | ||
import { ParentStep, QueryPropertyMetadata, QueryPropertiesMetadata } from "./query-properties-metadata"; | ||
import {ParentStep, QueryPropertiesMetadata, QueryPropertyMetadata} from "./query-properties-metadata"; | ||
@@ -6,0 +10,0 @@ export function makeQueryPropertiesMetadata |
@@ -1,4 +0,4 @@ | ||
import { caseNormalizeName, Nullable, relIdDescn } from './util/mod'; | ||
import { DatabaseMetadata, makeRelId, RelId, RelMetadata } from './dbmd'; | ||
import { SourceLanguage } from './source-generation-options'; | ||
import {caseNormalizeName, Nullable, relIdDescn} from './util/mod'; | ||
import {DatabaseMetadata, makeRelId, RelId, RelMetadata} from './dbmd'; | ||
import {SourceLanguage} from './source-generation-options'; | ||
@@ -20,3 +20,3 @@ export interface QueryGroupSpec | ||
resultRepresentations?: Nullable<ResultRepr[]>; | ||
additionalObjectPropertyColumns?: Nullable<AdditionalObjectPropertyColumn[]>; | ||
additionalOutputColumns?: Nullable<AdditionalOutputColumn[]>; // for JSON_OBJECT_ROWS only | ||
generateResultTypes?: Nullable<boolean>; | ||
@@ -29,2 +29,5 @@ generateSource?: Nullable<boolean>; | ||
// Additional output columns for top-level JSON_OBJECT_ROWS queries (added after the "json" column). | ||
export type AdditionalOutputColumn = string | TableFieldExpr; | ||
export type QueryTypesFileHeader = string | { [l in SourceLanguage]: string }; | ||
@@ -81,3 +84,3 @@ | ||
referenceName: undefined; | ||
subqueryAlias?: Nullable<string>; // alias for the FROM clause subquery, for use in record conditions | ||
fromEntryAlias?: Nullable<string>; // alias for the FROM entry in the child's query for the parent, for record conditions | ||
} | ||
@@ -99,3 +102,3 @@ | ||
equatedFields: FieldPair[]; | ||
// By default a custom match condition for a parent will cause a nullable parent reference or nullable inline | ||
// By default, a custom match condition for a parent will cause a nullable parent reference or nullable inline | ||
// fields from the parent. This option allows asserting that a matching parent record always exists for this | ||
@@ -124,5 +127,2 @@ // join condition, so the parent reference or inline fields can be non-nullable if other factors don't | ||
export type AdditionalObjectPropertyColumn = string | { property: string; as: string }; | ||
export function getInlineParentSpecs(tableSpec: TableJsonSpec): InlineParentSpec[] | ||
@@ -201,3 +201,4 @@ { | ||
( | ||
tableSpec: TableJsonSpec, | ||
fieldExpressions: Nullable<(string | TableFieldExpr)[]>, | ||
table: string, | ||
defaultSchema: Nullable<string>, | ||
@@ -208,7 +209,7 @@ dbmd: DatabaseMetadata, | ||
{ | ||
if (!tableSpec.fieldExpressions) | ||
if (!fieldExpressions) | ||
return; | ||
const simpleSelectFields : string[] = []; | ||
for ( const [ix, fieldExpr] of tableSpec.fieldExpressions.entries() ) | ||
for ( const [ix, fieldExpr] of fieldExpressions.entries() ) | ||
{ | ||
@@ -230,3 +231,3 @@ if (typeof fieldExpr === 'string') | ||
const relMd = getRelMetadata(tableSpec.table, defaultSchema, dbmd, specLoc); | ||
const relMd = getRelMetadata(table, defaultSchema, dbmd, specLoc); | ||
@@ -278,13 +279,2 @@ verifyFieldsExistInRelMd(simpleSelectFields, relMd, dbmd, specLoc); | ||
function verifyPropertyColumnsExist | ||
( | ||
propertyNames: string[], | ||
specLoc: SpecLocation | ||
) | ||
{ | ||
// TODO | ||
} | ||
function getRelMetadata | ||
@@ -291,0 +281,0 @@ ( |
@@ -1,9 +0,16 @@ | ||
import { RelId } from "../dbmd"; | ||
import {RelId} from "../dbmd"; | ||
import { | ||
ChildCollectionSelectEntry, ExpressionSelectEntry, FieldSelectEntry, getBaseTable, | ||
getPropertySelectEntries, InlineParentSelectEntry, ParentReferenceSelectEntry, QueryFromEntry, | ||
SelectEntry, SqlSpec | ||
ChildCollectionSelectEntry, | ||
ExpressionSelectEntry, | ||
FieldSelectEntry, | ||
getBaseTable, | ||
getPropertySelectEntries, | ||
InlineParentSelectEntry, | ||
ParentReferenceSelectEntry, | ||
QueryFromEntry, | ||
SelectEntry, | ||
SqlSpec | ||
} from "../sql-generation/sql-specs"; | ||
import { dedupedWithAssignedNames } from "./result-type-names-assignment"; | ||
import { NamedResultTypeSpec, ResultTypeProperty, ResultTypeSpec } from "./result-type-specs"; | ||
import {dedupedWithAssignedNames} from "./result-type-names-assignment"; | ||
import {NamedResultTypeSpec, ResultTypeProperty, ResultTypeSpec} from "./result-type-specs"; | ||
@@ -10,0 +17,0 @@ export function makeNamedResultTypeSpecs |
import {Nullable} from '../../util/nullable'; | ||
import {indentLines} from '../../util/strings'; | ||
import {SqlDialect} from './sql-dialect'; | ||
import {SelectEntry} from "../sql-specs"; | ||
import {generalSqlKeywordsLowercase, SelectEntry} from "../sql-specs"; | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const sqlKeywordsLowercase = new Set([ | ||
'select', 'from', 'where', 'user', 'order', 'group', 'by', 'over', 'is' | ||
]); | ||
export class HSQLDialect implements SqlDialect | ||
@@ -22,3 +16,3 @@ { | ||
selectEntries: SelectEntry[], | ||
srcAlias: string | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string | ||
) | ||
@@ -29,5 +23,4 @@ : string | ||
selectEntries | ||
.map(se=> | ||
`'${se.projectedName}' value ${srcAlias}.${this.quoteColumnNameIfNeeded(se.projectedName)}` + | ||
(needsFormatJsonQualifier(se) ? " format json" : "") | ||
.map(se=> `'${se.projectedName}' value ${selectEntryValueSqlFn(se)}` + | ||
(needsFormatJson(se) ? " format json" : "") | ||
) | ||
@@ -46,4 +39,4 @@ .join(',\n'); | ||
selectEntries: SelectEntry[], | ||
orderBy: Nullable<string>, | ||
srcAlias: string | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, | ||
orderBy: Nullable<string> | ||
) | ||
@@ -54,5 +47,5 @@ : string | ||
'coalesce(json_arrayagg(' + | ||
this.getRowObjectExpression(selectEntries, srcAlias) + | ||
(orderBy != null ? ' order by ' + orderBy.replace(/\$\$/g, srcAlias) : '') + | ||
`), '[]')` | ||
this.getRowObjectExpression(selectEntries, selectEntryValueSqlFn) + | ||
(orderBy != null ? ` order by ${orderBy}` : '') + | ||
`), '[]')` | ||
); | ||
@@ -63,14 +56,11 @@ } | ||
( | ||
selectEntry: SelectEntry, | ||
orderBy: Nullable<string>, | ||
srcAlias: string | ||
valueExpression: string, | ||
orderBy: Nullable<string> | ||
) | ||
: string | ||
{ | ||
const columnName = selectEntry.projectedName; | ||
return ( | ||
`coalesce(json_arrayagg(${srcAlias}.${this.quoteColumnNameIfNeeded(columnName)}` + | ||
(orderBy != null ? ` order by ${orderBy.replace(/\$\$/g, srcAlias)}` : '') + | ||
`), '[]')` | ||
`coalesce(jsonb_agg(${valueExpression}` + | ||
(orderBy != null ? ' order by ' + orderBy : '') + | ||
'))' | ||
); | ||
@@ -85,3 +75,3 @@ } | ||
!this.uppercaseNameRegex.test(name) || | ||
sqlKeywordsLowercase.has(name.toLowerCase()) ) | ||
generalSqlKeywordsLowercase.has(name.toLowerCase()) ) | ||
return `"${name}"`; | ||
@@ -95,3 +85,5 @@ return name; | ||
return nameExact; | ||
if ( !simpleIdentifierRegex.test(nameExact) || !this.uppercaseNameRegex.test(nameExact) ) | ||
if ( !simpleIdentifierRegex.test(nameExact) || | ||
!this.uppercaseNameRegex.test(nameExact) || | ||
avoidColumnNamesLowercase.has(nameExact.toLowerCase()) ) | ||
return `"${nameExact}"`; | ||
@@ -102,3 +94,3 @@ return nameExact; | ||
function needsFormatJsonQualifier(selectEntry: SelectEntry): boolean | ||
function needsFormatJson(selectEntry: SelectEntry): boolean | ||
{ | ||
@@ -115,5 +107,86 @@ switch (selectEntry.entryType) | ||
case "se-inline-parent-prop": | ||
return needsFormatJsonQualifier(selectEntry.parentSelectEntry); | ||
return needsFormatJson(selectEntry.parentSelectEntry); | ||
} | ||
} | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const avoidColumnNamesLowercase = new Set([ | ||
'all', | ||
'and', | ||
'and', | ||
'any', | ||
'as', | ||
'at', | ||
'between', | ||
'both', | ||
'by', | ||
'call', | ||
'case', | ||
'cast', | ||
'coalesce', | ||
'collation', | ||
'convert', | ||
'corresponding', | ||
'create', | ||
'cross', | ||
'cube', | ||
'current', | ||
'current_path', | ||
'default', | ||
'distinct', | ||
'do', | ||
'drop', | ||
'else', | ||
'every', | ||
'except', | ||
'exists', | ||
'fetch', | ||
'for', | ||
'from', | ||
'full', | ||
'grant', | ||
'group', | ||
'grouping', | ||
'having', | ||
'in', | ||
'inner', | ||
'intersect', | ||
'into', | ||
'is', | ||
'join', | ||
'leading', | ||
'left', | ||
'like', | ||
'natural', | ||
'normalize', | ||
'not', | ||
'nullif', | ||
'on', | ||
'or', | ||
'order', | ||
'outer', | ||
'primary', | ||
'references', | ||
'right', | ||
'rollup', | ||
'row', | ||
'select', | ||
'set', | ||
'some', | ||
'sum', | ||
'table', | ||
'then', | ||
'to', | ||
'trailing', | ||
'trigger', | ||
'union', | ||
'unique', | ||
'user', | ||
'using', | ||
'values', | ||
'when', | ||
'where', | ||
'with', | ||
]); | ||
import {Nullable} from '../../util/nullable'; | ||
import {indentLines, unDoubleQuote} from '../../util/strings'; | ||
import {SqlDialect} from './sql-dialect'; | ||
import {SelectEntry} from "../sql-specs"; | ||
import {generalSqlKeywordsLowercase, SelectEntry} from "../sql-specs"; | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const sqlKeywordsLowercase = new Set([ | ||
'select', 'from', 'where', 'user', 'order', 'group', 'by', 'over', 'is' | ||
]); | ||
export class MySQLDialect implements SqlDialect | ||
@@ -22,11 +16,9 @@ { | ||
selectEntries: SelectEntry[], | ||
srcAlias: string | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string | ||
) | ||
: string | ||
{ | ||
const columnNames = selectEntries.map(e => e.projectedName); | ||
const objectFieldDecls = | ||
columnNames | ||
.map(colName => `'${colName}', ${srcAlias}.${this.quoteColumnNameIfNeeded(colName)}`) | ||
selectEntries | ||
.map(se => `'${se.projectedName}', ${selectEntryValueSqlFn(se)}`) | ||
.join(',\n'); | ||
@@ -44,4 +36,4 @@ | ||
selectEntries: SelectEntry[], | ||
orderBy: Nullable<string>, | ||
srcAlias: string | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, | ||
orderBy: Nullable<string> | ||
) | ||
@@ -55,3 +47,3 @@ : string | ||
'cast(coalesce(json_arrayagg(' + | ||
this.getRowObjectExpression(selectEntries, srcAlias) + | ||
this.getRowObjectExpression(selectEntries, selectEntryValueSqlFn) + | ||
`), json_type('[]')) as json)` | ||
@@ -63,16 +55,11 @@ ); | ||
( | ||
selectEntry: SelectEntry, | ||
orderBy: Nullable<string>, | ||
srcAlias: string | ||
valueExpression: string, | ||
orderBy: Nullable<string> | ||
) | ||
: string | ||
{ | ||
const columnName = selectEntry.projectedName; | ||
if (orderBy != null ) | ||
throw new Error(`Error for column ${columnName}: MySQL dialect does not support ordering in aggregate functions currently.`); | ||
throw new Error(`MySQL dialect does not support ordering in aggregate functions currently.`); | ||
return ( | ||
`cast(coalesce(json_arrayagg(${srcAlias}.${this.quoteColumnNameIfNeeded(columnName)}), json_type('[]')) as json)` | ||
); | ||
return (`cast(coalesce(json_arrayagg(${valueExpression}), json_type('[]')) as json)`); | ||
} | ||
@@ -86,3 +73,4 @@ | ||
return `\`${unDoubleQuote(name)}\``; | ||
if ( !simpleIdentifierRegex.test(name) || sqlKeywordsLowercase.has(name.toLowerCase()) ) | ||
if ( !simpleIdentifierRegex.test(name) || | ||
generalSqlKeywordsLowercase.has(name.toLowerCase()) ) | ||
return `\`${name}\``; | ||
@@ -98,3 +86,4 @@ return name; | ||
return `\`${unDoubleQuote(name)}\``; | ||
if ( !simpleIdentifierRegex.test(name) ) | ||
if ( !simpleIdentifierRegex.test(name) || | ||
avoidColumnNamesLowercase.has(name.toLowerCase()) ) | ||
return `\`${name}\``; | ||
@@ -104,1 +93,249 @@ return name; | ||
} | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const avoidColumnNamesLowercase = new Set([ | ||
'add', | ||
'all', | ||
'alter', | ||
'analyze', | ||
'and', | ||
'as', | ||
'asc', | ||
'asensitive', | ||
'before', | ||
'between', | ||
'bigint', | ||
'binary', | ||
'blob', | ||
'both', | ||
'by', | ||
'call', | ||
'cascade', | ||
'case', | ||
'change', | ||
'char', | ||
'character', | ||
'check', | ||
'collate', | ||
'column', | ||
'condition', | ||
'constraint', | ||
'continue', | ||
'convert', | ||
'create', | ||
'cross', | ||
'cube', | ||
'cume_dist', | ||
'current_date', | ||
'current_time', | ||
'current_timestamp', | ||
'current_user', | ||
'cursor', | ||
'database', | ||
'databases', | ||
'day_hour', | ||
'day_microsecond', | ||
'day_minute', | ||
'day_second', | ||
'dec', | ||
'decimal', | ||
'declare', | ||
'default', | ||
'delayed', | ||
'delete', | ||
'dense_rank', | ||
'desc', | ||
'describe', | ||
'deterministic', | ||
'distinct', | ||
'distinctrow', | ||
'div', | ||
'double', | ||
'drop', | ||
'dual', | ||
'each', | ||
'else', | ||
'elseif', | ||
'enclosed', | ||
'escaped', | ||
'except', | ||
'exists', | ||
'exit', | ||
'explain', | ||
'false', | ||
'fetch', | ||
'float', | ||
'float4', | ||
'float8', | ||
'for', | ||
'force', | ||
'foreign', | ||
'from', | ||
'fulltext', | ||
'function', | ||
'generated', | ||
'get', | ||
'grant', | ||
'group', | ||
'grouping', | ||
'having', | ||
'high_priority', | ||
'hour_microsecond', | ||
'hour_minute', | ||
'hour_second', | ||
'if', | ||
'ignore', | ||
'in', | ||
'index', | ||
'infile', | ||
'inner', | ||
'inout', | ||
'insensitive', | ||
'insert', | ||
'int', | ||
'int1', | ||
'int2', | ||
'int3', | ||
'int4', | ||
'int8', | ||
'integer', | ||
'interval', | ||
'into', | ||
'is', | ||
'iterate', | ||
'join', | ||
'key', | ||
'keys', | ||
'kill', | ||
'lateral', | ||
'lead', | ||
'leading', | ||
'leave', | ||
'left', | ||
'like', | ||
'limit', | ||
'lines', | ||
'load', | ||
'localtime', | ||
'localtimestamp', | ||
'lock', | ||
'long', | ||
'longblob', | ||
'longtext', | ||
'loop', | ||
'low_priority', | ||
'match', | ||
'mediumblob', | ||
'mediumint', | ||
'mediumtext', | ||
'middleint', | ||
'minute_microsecond', | ||
'minute_second', | ||
'mod', | ||
'modifies', | ||
'natural', | ||
'no_write_to_binlog', | ||
'not', | ||
'null', | ||
'numeric', | ||
'of', | ||
'on', | ||
'optimize', | ||
'option', | ||
'optionally', | ||
'or', | ||
'order', | ||
'out', | ||
'outer', | ||
'outfile', | ||
'over', | ||
'partition', | ||
'percent_rank', | ||
'precision', | ||
'primary', | ||
'procedure', | ||
'purge', | ||
'range', | ||
'rank', | ||
'read', | ||
'reads', | ||
'real', | ||
'recursive', | ||
'references', | ||
'regexp', | ||
'release', | ||
'rename', | ||
'repeat', | ||
'replace', | ||
'require', | ||
'require', | ||
'resignal', | ||
'restrict', | ||
'return', | ||
'revoke', | ||
'right', | ||
'rlike', | ||
'row', | ||
'row_number', | ||
'rows', | ||
'schema', | ||
'schemas', | ||
'second_microsecond', | ||
'select', | ||
'sensitive', | ||
'separator', | ||
'set', | ||
'signal', | ||
'show', | ||
'smallint', | ||
'spatial', | ||
'specific', | ||
'sql', | ||
'sql_big_result', | ||
'sql_calc_found_rows', | ||
'sql_small_result', | ||
'sqlexception', | ||
'sqlstate', | ||
'sqlwarning', | ||
'ssl', | ||
'starting', | ||
'straight_join', | ||
'system', | ||
'table', | ||
'terminated', | ||
'then', | ||
'tinyblob', | ||
'tinyint', | ||
'tinytext', | ||
'to', | ||
'trailing', | ||
'trigger', | ||
'true', | ||
'undo', | ||
'union', | ||
'unique', | ||
'unlock', | ||
'unsigned', | ||
'update', | ||
'usage', | ||
'use', | ||
'using', | ||
'utc_date', | ||
'utc_time', | ||
'utc_timestamp', | ||
'values', | ||
'varbinary', | ||
'varchar', | ||
'varcharacter', | ||
'varying', | ||
'when', | ||
'where', | ||
'while', | ||
'window', | ||
'with', | ||
'write', | ||
'xor', | ||
'year_month', | ||
'zerofill' | ||
]); |
import {Nullable} from '../../util/nullable'; | ||
import {indentLines} from '../../util/strings'; | ||
import {SqlDialect} from './sql-dialect'; | ||
import {SelectEntry} from "../sql-specs"; | ||
import {generalSqlKeywordsLowercase, SelectEntry} from "../sql-specs"; | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const sqlKeywordsLowercase = new Set([ | ||
'select', 'from', 'where', 'user', 'order', 'group', 'by', 'over', 'is' | ||
]); | ||
export class OracleDialect implements SqlDialect | ||
@@ -22,11 +16,9 @@ { | ||
selectEntries: SelectEntry[], | ||
srcAlias: string | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string | ||
) | ||
: string | ||
{ | ||
const columnNames = selectEntries.map(e => e.projectedName); | ||
const objectFieldDecls = | ||
columnNames | ||
.map(colName => `'${colName}' value ${srcAlias}.${this.quoteColumnNameIfNeeded(colName)}`) | ||
selectEntries | ||
.map(se => `'${se.projectedName}' value ${selectEntryValueSqlFn(se)}`) | ||
.join(',\n'); | ||
@@ -44,4 +36,4 @@ | ||
selectEntries: SelectEntry[], | ||
orderBy: Nullable<string>, | ||
srcAlias: string | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, | ||
orderBy: Nullable<string> | ||
) | ||
@@ -52,4 +44,4 @@ : string | ||
'treat(coalesce(json_arrayagg(' + | ||
this.getRowObjectExpression(selectEntries, srcAlias) + | ||
(orderBy != null ? ' order by ' + orderBy.replace(/\$\$/g, srcAlias) : '') + | ||
this.getRowObjectExpression(selectEntries, selectEntryValueSqlFn) + | ||
(orderBy != null ? ` order by ${orderBy}` : '') + | ||
` returning clob), to_clob('[]')) as json)` | ||
@@ -61,13 +53,10 @@ ); | ||
( | ||
selectEntry: SelectEntry, | ||
orderBy: Nullable<string>, | ||
srcAlias: string | ||
valueExpression: string, | ||
orderBy: Nullable<string> | ||
) | ||
: string | ||
{ | ||
const columnName = selectEntry.projectedName; | ||
return ( | ||
`treat(coalesce(json_arrayagg(${srcAlias}.${this.quoteColumnNameIfNeeded(columnName)}` + | ||
(orderBy != null ? ` order by ${orderBy.replace(/\$\$/g, srcAlias)}` : '') + | ||
`treat(coalesce(json_arrayagg(${valueExpression}` + | ||
(orderBy != null ? ` order by ${orderBy}` : '') + | ||
` returning clob), to_clob('[]')) as json)` | ||
@@ -83,3 +72,3 @@ ); | ||
!this.uppercaseNameRegex.test(name) || | ||
sqlKeywordsLowercase.has(name.toLowerCase()) ) | ||
generalSqlKeywordsLowercase.has(name.toLowerCase()) ) | ||
return `"${name}"`; | ||
@@ -89,10 +78,125 @@ return name; | ||
quoteColumnNameIfNeeded(nameExact: string): string | ||
quoteColumnNameIfNeeded(name: string): string | ||
{ | ||
if ( this.quotedStringRegex.test(nameExact) ) | ||
return nameExact; | ||
if ( !simpleIdentifierRegex.test(nameExact) || !this.uppercaseNameRegex.test(nameExact) ) | ||
return `"${nameExact}"`; | ||
return nameExact; | ||
if ( this.quotedStringRegex.test(name) ) | ||
return name; | ||
if ( !simpleIdentifierRegex.test(name) || | ||
!this.uppercaseNameRegex.test(name) || | ||
avoidColumnNamesLowercase.has(name.toLowerCase()) ) | ||
return `"${name}"`; | ||
return name; | ||
} | ||
} | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const avoidColumnNamesLowercase = new Set([ | ||
'access', | ||
'add', | ||
'all', | ||
'alter', | ||
'and', | ||
'any', | ||
'as', | ||
'asc', | ||
'audit', | ||
'between', | ||
'by', | ||
'char', | ||
'check', | ||
'cluster', | ||
'column', | ||
'comment', | ||
'compress', | ||
'connect', | ||
'create', | ||
'current', | ||
'date', | ||
'decimal', | ||
'default', | ||
'delete', | ||
'desc', | ||
'distinct', | ||
'drop', | ||
'else', | ||
'exclusive', | ||
'exists', | ||
'file', | ||
'float', | ||
'for', | ||
'from', | ||
'grant', | ||
'group', | ||
'having', | ||
'identified', | ||
'immediate', | ||
'in', | ||
'increment', | ||
'index', | ||
'initial', | ||
'insert', | ||
'integer', | ||
'intersect', | ||
'into', | ||
'is', | ||
'level', | ||
'like', | ||
'lock', | ||
'long', | ||
'maxextents', | ||
'minus', | ||
'mlslabel', | ||
'mode', | ||
'modify', | ||
'noaudit', | ||
'nocompress', | ||
'not', | ||
'nowait', | ||
'null', | ||
'number', | ||
'of', | ||
'offline', | ||
'on', | ||
'online', | ||
'option', | ||
'or', | ||
'order', | ||
'pctfree', | ||
'prior', | ||
'public', | ||
'raw', | ||
'rename', | ||
'resource', | ||
'revoke', | ||
'row', | ||
'rowid', | ||
'rownum', | ||
'rows', | ||
'select', | ||
'session', | ||
'set', | ||
'share', | ||
'size', | ||
'smallint', | ||
'start', | ||
'successful', | ||
'synonym', | ||
'sysdate', | ||
'table', | ||
'then', | ||
'to', | ||
'trigger', | ||
'uid', | ||
'union', | ||
'unique', | ||
'update', | ||
'user', | ||
'validate', | ||
'values', | ||
'varchar', | ||
'varchar2', | ||
'view', | ||
'whenever', | ||
'where', | ||
'with', | ||
]); |
import {Nullable} from '../../util/nullable'; | ||
import {indentLines} from '../../util/strings'; | ||
import {SqlDialect} from './sql-dialect'; | ||
import {SelectEntry} from "../sql-specs"; | ||
import {generalSqlKeywordsLowercase, SelectEntry} from "../sql-specs"; | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const sqlKeywordsLowercase = new Set([ | ||
'select', 'from', 'where', 'user', 'order', 'group', 'by', 'over', 'is' | ||
]); | ||
export class PostgresDialect implements SqlDialect | ||
@@ -22,11 +16,9 @@ { | ||
selectEntries: SelectEntry[], | ||
srcAlias: string | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string | ||
) | ||
: string | ||
{ | ||
const columnNames = selectEntries.map(e => e.projectedName); | ||
const objectFieldDecls = | ||
columnNames | ||
.map(colName => `'${colName}', ${srcAlias}.${this.quoteColumnNameIfNeeded(colName)}`) | ||
selectEntries | ||
.map(se => `'${se.projectedName}', ${selectEntryValueSqlFn(se)}`) | ||
.join(',\n'); | ||
@@ -44,4 +36,4 @@ | ||
selectEntries: SelectEntry[], | ||
orderBy: Nullable<string>, | ||
srcAlias: string | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, | ||
orderBy: Nullable<string> | ||
) | ||
@@ -52,4 +44,4 @@ : string | ||
'coalesce(jsonb_agg(' + | ||
this.getRowObjectExpression(selectEntries, srcAlias) + | ||
(orderBy != null ? ` order by ${orderBy.replace(/\$\$/g, srcAlias)}` : '') + | ||
this.getRowObjectExpression(selectEntries, selectEntryValueSqlFn) + | ||
(orderBy != null ? ` order by ${orderBy}` : '') + | ||
`),'[]'::jsonb)` | ||
@@ -61,13 +53,10 @@ ); | ||
( | ||
selectEntry: SelectEntry, | ||
orderBy: Nullable<string>, | ||
srcAlias: string | ||
valueExpression: string, | ||
orderBy: Nullable<string> | ||
) | ||
: string | ||
{ | ||
const columnName = selectEntry.projectedName; | ||
return ( | ||
`coalesce(jsonb_agg(${srcAlias}.${this.quoteColumnNameIfNeeded(columnName)}` + | ||
(orderBy != null ? ' order by ' + orderBy.replace(/\$\$/g, srcAlias) : '') + | ||
`coalesce(jsonb_agg(${valueExpression}` + | ||
(orderBy != null ? ' order by ' + orderBy : '') + | ||
'))' | ||
@@ -83,3 +72,3 @@ ); | ||
!this.lowercaseNameRegex.test(name) || | ||
sqlKeywordsLowercase.has(name) ) | ||
generalSqlKeywordsLowercase.has(name.toLowerCase()) ) | ||
return `"${name}"`; | ||
@@ -93,6 +82,106 @@ return name; | ||
return name; | ||
if ( !simpleIdentifierRegex.test(name) || !this.lowercaseNameRegex.test(name) ) | ||
if ( !simpleIdentifierRegex.test(name) || | ||
!this.lowercaseNameRegex.test(name) || | ||
avoidColumnNamesLowercase.has(name.toLowerCase()) ) | ||
return `"${name}"`; | ||
return name; | ||
} | ||
} | ||
} | ||
const simpleIdentifierRegex = new RegExp(/^[A-Za-z][A-Za-z0-9_]+$/); | ||
const avoidColumnNamesLowercase = new Set([ | ||
'all', | ||
'analyze', | ||
'and', | ||
'any', | ||
'array', | ||
'as', | ||
'asymmetric', | ||
'authorization', | ||
'binary', | ||
'both', | ||
'case', | ||
'cast', | ||
'check', | ||
'collate', | ||
'collation', | ||
'column', | ||
'constraint', | ||
'create', | ||
'cross', | ||
'current_date', | ||
'current_role', | ||
'current_time', | ||
'current_timestamp', | ||
'current_user', | ||
'default', | ||
'deferrable', | ||
'desc', | ||
'distinct', | ||
'do', | ||
'else', | ||
'end', | ||
'except', | ||
'false', | ||
'fetch', | ||
'for', | ||
'foreign', | ||
'freeze', | ||
'from', | ||
'full', | ||
'grant', | ||
'group', | ||
'having', | ||
'ilike', | ||
'in', | ||
'initially', | ||
'inner', | ||
'intersect', | ||
'interval', | ||
'into', | ||
'is', | ||
'isnull', | ||
'join', | ||
'lateral', | ||
'leading', | ||
'left', | ||
'like', | ||
'limit', | ||
'localtime', | ||
'localtimestamp', | ||
'natural', | ||
'not', | ||
'notnull', | ||
'null', | ||
'offset', | ||
'on', | ||
'only', | ||
'or', | ||
'order', | ||
'outer', | ||
'overlaps', | ||
'placing', | ||
'primary', | ||
'references', | ||
'right', | ||
'select', | ||
'session_user', | ||
'similar', | ||
'some', | ||
'symmetric', | ||
'table', | ||
'tablesample', | ||
'then', | ||
'to', | ||
'trailing', | ||
'true', | ||
'union', | ||
'unique', | ||
'user', | ||
'using', | ||
'verbose', | ||
'when', | ||
'where', | ||
'with' | ||
]); |
@@ -6,7 +6,23 @@ import {Nullable} from '../../util/mod'; | ||
{ | ||
getRowObjectExpression(selectEntries: SelectEntry[], srcAlias: string): string; | ||
getRowObjectExpression | ||
( | ||
selectEntries: SelectEntry[], | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string | ||
) | ||
: string; | ||
getAggregatedRowObjectsExpression(selectEntries: SelectEntry[], orderBy: Nullable<string>, srcAlias: string): string; | ||
getAggregatedRowObjectsExpression | ||
( | ||
selectEntries: SelectEntry[], | ||
selectEntryValueSqlFn: (selectEntry: SelectEntry) => string, | ||
orderBy: Nullable<string> | ||
) | ||
: string; | ||
getAggregatedColumnValuesExpression(selectEntry: SelectEntry, orderBy: Nullable<string>, srcAlias: string): string; | ||
getAggregatedColumnValuesExpression | ||
( | ||
valueExpression: string, | ||
orderBy: Nullable<string> | ||
) | ||
: string; | ||
@@ -13,0 +29,0 @@ quoteObjectNameIfNeeded(name: string): string; |
import {indentLines, replaceAll} from "../util/strings"; | ||
import {isNonEmpty, mapSet, sorted} from "../util/collections"; | ||
import {mapSet, nonEmpty, sorted} from "../util/collections"; | ||
import {exactUnquotedName} from "../util/database-names"; | ||
import {CaseSensitivity, RelId} from "../dbmd"; | ||
import { | ||
FromEntry, | ||
getPropertySelectEntries, | ||
OrderBy, | ||
ParentChildCondition, | ||
SelectEntry, | ||
SqlSpec, | ||
WhereEntry | ||
} from "./sql-specs"; | ||
import {FromEntry, getPropertySelectEntries, ParentChildCondition, SelectEntry, SqlSpec, WhereEntry} from "./sql-specs"; | ||
import {SqlDialect} from "./sql-dialects"; | ||
import {AdditionalObjectPropertyColumn} from "../query-specs"; | ||
import {Nullable} from "../util/mod"; | ||
@@ -35,40 +25,35 @@ export class SqlSourceGenerator | ||
{ | ||
const baseSql = this.baseSql(spec); | ||
const propSelEntries = getPropertySelectEntries(spec); | ||
const objWrap = spec.objectWrapProperties ?? false; | ||
const arrayAgg = spec.aggregateToArray ?? false; | ||
const addSelEntries = spec.additionalOutputSelectEntries ?? []; | ||
const selectComment = this.genComments && spec.selectEntriesLeadingComment ? | ||
`-- ${spec.selectEntriesLeadingComment}\n` : ''; | ||
const wrapPropsInObj = spec.objectWrapProperties ?? false; | ||
const selEntryValFn = (se: SelectEntry) => this.selectEntrySql(se, true); | ||
if (!spec.aggregateToArray && !wrapPropsInObj) // no aggregation nor object wrapping | ||
{ | ||
return baseSql; | ||
} | ||
else // at least one of aggregation and object wrapping of properties will be done | ||
{ | ||
const propSelectEntries = getPropertySelectEntries(spec); | ||
const baseTable = baseTableDescn(spec); // only used for comments | ||
const selectEntries = | ||
arrayAgg ? | ||
(objWrap | ||
? this.sqlDialect.getAggregatedRowObjectsExpression(propSelEntries, selEntryValFn, spec.aggregateOrderBy) | ||
: this.sqlDialect.getAggregatedColumnValuesExpression(this.selectEntrySql(propSelEntries[0], true), spec.aggregateOrderBy))+' json\n' | ||
: objWrap ? | ||
this.sqlDialect.getRowObjectExpression(propSelEntries, selEntryValFn)+' json' + | ||
(nonEmpty(addSelEntries) | ||
? `,\n${addSelEntries.map(c => this.selectEntrySql(c)).join(',\n')}` | ||
: '' | ||
) + "\n" | ||
: this.selectEntriesSql(spec.selectEntries) + "\n"; | ||
if (!spec.aggregateToArray) // no aggregation but do object wrapping | ||
{ | ||
const additionalCols = spec.additionalObjectPropertyColumns ?? []; | ||
return this.jsonRowObjectsSql(baseSql, propSelectEntries, additionalCols, spec.orderBy, baseTable); | ||
} | ||
else // aggregation and maybe object wrapping | ||
{ | ||
return this.aggregateSql(baseSql, propSelectEntries, wrapPropsInObj, spec.orderBy, baseTable); | ||
} | ||
} | ||
} | ||
private baseSql(spec: SqlSpec) | ||
{ | ||
const selectComment = this.genComments && spec.selectEntriesLeadingComment ? | ||
`-- ${spec.selectEntriesLeadingComment}\n` : ''; | ||
const selectEntries = this.makeSelectEntriesSql(spec.selectEntries) + '\n'; | ||
const fromComment = this.genComments && spec.fromEntriesLeadingComment ? | ||
`-- ${spec.fromEntriesLeadingComment}\n` : ''; | ||
const fromEntries = spec.fromEntries.map(e => this.fromEntrySql(e)).join('\n') + '\n'; | ||
const whereClause = isNonEmpty(spec.whereEntries) | ||
? `where (\n${this.indent(spec.whereEntries.map(e => this.whereEntrySql(e)).join(' and\n'))}\n)\n` | ||
const whereClause = nonEmpty(spec.whereEntries) ? | ||
`where (\n${this.indent(spec.whereEntries.map(e => this.whereEntrySql(e)).join(' and\n'))}\n)\n` | ||
: ''; | ||
const orderByClause = spec.orderBy | ||
? 'order by ' + spec.orderBy.orderBy.replace(/\$\$/g, spec.orderBy.tableAlias) + '\n' | ||
const orderByClause = spec.orderBy ? | ||
'order by ' + spec.orderBy.orderBy.replace(/\$\$/g, spec.orderBy.tableAlias) + '\n' | ||
: ''; | ||
@@ -84,3 +69,3 @@ | ||
private makeSelectEntriesSql(specSelectEntries: SelectEntry[]): string | ||
private selectEntriesSql(specSelectEntries: SelectEntry[]): string | ||
{ | ||
@@ -94,79 +79,16 @@ // Assign any missing displayOrders based on select entry position. | ||
const sortedSelectEntries = sorted(selectEntries, (e1, e2) => (e1.displayOrder ?? 0) - (e2.displayOrder ?? 0)); | ||
const sortedSelectEntries = | ||
sorted( | ||
selectEntries, | ||
(e1, e2) => (e1.displayOrder ?? 0) - (e2.displayOrder ?? 0) | ||
); | ||
return sortedSelectEntries.map(e => this.selectEntrySql(e)).join(',\n'); | ||
} | ||
private jsonRowObjectsSql | ||
( | ||
baseSql: string, | ||
selectEntries: SelectEntry[], | ||
additionalColumns: AdditionalObjectPropertyColumn[], | ||
orderBy: Nullable<OrderBy>, | ||
baseTableDesc: Nullable<string>, // for comments | ||
) | ||
: string | ||
{ | ||
return ( | ||
'select\n' + | ||
this.indent( | ||
(baseTableDesc ? `-- row object for table '${baseTableDesc}'\n` : '') + | ||
this.sqlDialect.getRowObjectExpression(selectEntries, 'q') + ' json' + | ||
(isNonEmpty(additionalColumns) | ||
? `,\n${additionalColumns.map(c => this.additionalPropertyColumnSql(c)).join(',\n')}` | ||
: '') | ||
) + '\n' + | ||
'from (\n' + | ||
nlterm(this.indent( | ||
(baseTableDesc ? `-- base query for table '${baseTableDesc}'\n` : '') + | ||
baseSql | ||
)) + | ||
') q' + | ||
(orderBy != null ? '\norder by ' + orderBy.orderBy.replace(/\$\$/g, 'q') : '') | ||
return sortedSelectEntries.map(se => | ||
(this.genComments && se.comment ? `-- ${se.comment}\n` : '') + | ||
this.selectEntrySql(se)).join(',\n' | ||
); | ||
} | ||
private additionalPropertyColumnSql(c: AdditionalObjectPropertyColumn): string | ||
private selectEntrySql(selectEntry: SelectEntry, valueOnly = false): string | ||
{ | ||
if (typeof(c) === 'string') | ||
return this.sqlDialect.quoteColumnNameIfNeeded(c); | ||
const propNameExpr = this.sqlDialect.quoteColumnNameIfNeeded(c.property); | ||
const alias = this.sqlDialect.quoteColumnNameIfNeeded(c.as); | ||
return `${propNameExpr} as ${alias}`; | ||
} | ||
private aggregateSql | ||
( | ||
sql: string, | ||
selectEntries: SelectEntry[], | ||
wrapProps: boolean, | ||
orderBy: Nullable<OrderBy>, | ||
baseTableDesc: Nullable<string>, // for comments | ||
) | ||
: string | ||
{ | ||
const ordby = orderBy?.orderBy; | ||
return ( | ||
'select\n' + | ||
this.indent( | ||
(this.genComments ? `-- aggregated ${wrapProps? 'rows' : 'values'} from table '${baseTableDesc}'\n`: '') + | ||
(wrapProps | ||
? this.sqlDialect.getAggregatedRowObjectsExpression(selectEntries, ordby, 'q') | ||
: this.sqlDialect.getAggregatedColumnValuesExpression(selectEntries[0], ordby, 'q')) + ' json\n' | ||
) + | ||
'from (\n' + | ||
nlterm(this.indent( | ||
(this.genComments ? `-- base query for table '${baseTableDesc}'\n` : '') + | ||
sql | ||
)) + | ||
') q' | ||
); | ||
} | ||
private selectEntrySql(selectEntry: SelectEntry): string | ||
{ | ||
const projectedName = this.maybeQuoteColumn(selectEntry.projectedName); | ||
const isProjectedNameQuoted = projectedName !== selectEntry.projectedName; | ||
switch (selectEntry.entryType) | ||
@@ -176,7 +98,27 @@ { | ||
{ | ||
const fieldName = this.maybeQuoteColumn(selectEntry.field.name); | ||
const fieldName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.field.name); | ||
const valueSql = `${selectEntry.tableAlias}.${fieldName}`; | ||
if (valueOnly) | ||
return valueSql; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
const isProjectedNameQuoted = projectedName !== selectEntry.projectedName; | ||
const sep = isProjectedNameQuoted ? ' ' : ' as '; | ||
return `${selectEntry.tableAlias}.${fieldName}${sep}${projectedName}`; | ||
return `${valueSql}${sep}${projectedName}`; | ||
} | ||
case 'se-hidden-pkf': | ||
{ | ||
const fieldName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.pkFieldName); | ||
const valueSql = `${selectEntry.tableAlias}.${fieldName}`; | ||
if (valueOnly) | ||
return valueSql; | ||
const isFieldNameQuoted = fieldName !== selectEntry.pkFieldName; | ||
const sep = isFieldNameQuoted ? ' ' : ' as '; | ||
const exportedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
return `${valueSql}${sep}${exportedName}`; | ||
} | ||
case 'se-expr': | ||
@@ -186,2 +128,7 @@ { | ||
const expr = replaceAll(selectEntry.expression, tableAliasPlaceholder, selectEntry.tableAlias); | ||
if (valueOnly) | ||
return expr; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
const isProjectedNameQuoted = projectedName !== selectEntry.projectedName; | ||
const sep = isProjectedNameQuoted ? ' ' : ' as '; | ||
@@ -193,4 +140,4 @@ | ||
{ | ||
return (this.genComments && selectEntry.comment ? `-- ${selectEntry.comment}\n` : '') + | ||
`${selectEntry.parentAlias}.${projectedName}`; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
return `${selectEntry.parentAlias}.${projectedName}`; | ||
} | ||
@@ -201,6 +148,9 @@ case 'se-parent-ref': | ||
return (this.genComments && selectEntry.comment ? `-- ${selectEntry.comment}\n` : '') + | ||
'(\n' + | ||
this.indent(parentRowObjSql) + '\n' + | ||
`) ${projectedName}`; | ||
const valueSql = '(\n'+this.indent(parentRowObjSql)+')'; | ||
if (valueOnly) | ||
return valueSql; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
return `${valueSql} ${projectedName}`; | ||
} | ||
@@ -211,15 +161,10 @@ case 'se-child-coll': | ||
return (this.genComments && selectEntry.comment ? `-- ${selectEntry.comment}\n` : '') + | ||
'(\n' + | ||
this.indent(collectionSql) + '\n' + | ||
`) ${projectedName}`; | ||
const valueSql = '(\n'+this.indent(collectionSql)+')'; | ||
if (valueOnly) | ||
return valueSql; | ||
const projectedName = this.sqlDialect.quoteColumnNameIfNeeded(selectEntry.projectedName); | ||
return `${valueSql} ${projectedName}`; | ||
} | ||
case 'se-hidden-pkf': | ||
{ | ||
const fieldName = this.maybeQuoteColumn(selectEntry.pkFieldName); | ||
const isFieldNameQuoted = fieldName !== selectEntry.pkFieldName; | ||
const sep = isFieldNameQuoted ? ' ' : ' as '; | ||
const exportedName = this.maybeQuoteColumn(selectEntry.projectedName); | ||
return `${selectEntry.tableAlias}.${fieldName}${sep}${exportedName}`; | ||
} | ||
} | ||
@@ -277,5 +222,5 @@ } | ||
return pcCond.matchedFields.map(mf => | ||
`${childAlias}.${this.maybeQuoteColumn(mf.foreignKeyFieldName)}` + | ||
`${childAlias}.${this.sqlDialect.quoteColumnNameIfNeeded(mf.foreignKeyFieldName)}` + | ||
' = ' + | ||
`${parentAlias}.${this.maybeQuoteColumn(mf.primaryKeyFieldName)}` | ||
`${parentAlias}.${this.sqlDialect.quoteColumnNameIfNeeded(mf.primaryKeyFieldName)}` | ||
).join(' and '); | ||
@@ -296,22 +241,4 @@ } | ||
} | ||
private maybeQuoteColumn(colName: string) | ||
{ | ||
return this.sqlDialect.quoteColumnNameIfNeeded(colName); | ||
} | ||
} | ||
function baseTableDescn(sqlSpec: SqlSpec): string | null | ||
{ | ||
const firstFromEntry = sqlSpec.fromEntries[0]; | ||
if (firstFromEntry.entryType === 'table') return firstFromEntry.table.name; | ||
return null; | ||
} | ||
// Newline terminate the given string if it doesn't end with a newline. | ||
function nlterm(s: string): string | ||
{ | ||
return !s.endsWith('\n') ? s + '\n' : s; | ||
} | ||
const DEFAULT_ALIAS_PLACEHOLDER = '$$'; |
@@ -1,5 +0,5 @@ | ||
import {caseNormalizeName, exactUnquotedName, makeMap, makeNameNotInSet, Nullable, relIdDescn} from '../util/mod'; | ||
import {caseNormalizeName, exactUnquotedName, makeMap, Nullable, relIdDescn} from '../util/mod'; | ||
import {DatabaseMetadata, Field, ForeignKey, foreignKeyFieldNames, RelId} from '../dbmd'; | ||
import { | ||
AdditionalObjectPropertyColumn, | ||
AdditionalOutputColumn, | ||
addLocPart, | ||
@@ -25,6 +25,8 @@ ChildSpec, | ||
ChildForeignKeyCondition, | ||
createTableAlias, | ||
ExpressionSelectEntry, | ||
FieldSelectEntry, | ||
getPropertySelectEntries, | ||
HiddenPrimaryKeySelectEntry, | ||
ParentPrimaryKeyCondition, | ||
SelectEntry, | ||
SqlParts, | ||
@@ -72,4 +74,4 @@ SqlSpec, | ||
case 'JSON_OBJECT_ROWS': | ||
const additionalCols = query.additionalObjectPropertyColumns ?? []; | ||
return this.jsonObjectRowsSql(query.tableJson, additionalCols, null, query.orderBy, specLoc); | ||
const addCols = query.additionalOutputColumns ?? []; | ||
return this.jsonObjectRowsSql(query.tableJson, addCols, null, query.orderBy, specLoc); | ||
case 'JSON_ARRAY_ROW': | ||
@@ -88,3 +90,3 @@ return this.jsonArrayRowSql(query.tableJson, null, false, query.orderBy, specLoc); | ||
specLoc: SpecLocation, | ||
exportPkFieldsHidden?: 'export-pk-fields-hidden', | ||
exportPkFieldsHidden?: 'export-pk-fields-hidden' | ||
) | ||
@@ -111,3 +113,3 @@ : SqlSpec | ||
sqlb.addSelectEntries(this.tableFieldExpressionSelectEntries(tjs, alias, specLoc)); | ||
sqlb.addSelectEntries(this.tableFieldExpressionSelectEntries(tjs.fieldExpressions, tjs.table, alias, specLoc)); | ||
@@ -146,8 +148,8 @@ sqlb.addParts(this.inlineParentsSqlParts(tjs, relId, alias, sqlb.getAliases(), specLoc)); | ||
/// Make query SQL having JSON object result values at the top level of the | ||
/// result set. The query returns a JSON value in a single column and with | ||
/// any number of result rows. | ||
/// result set. The query returns a JSON value in a single column for each | ||
/// of any number of result rows. | ||
private jsonObjectRowsSql | ||
( | ||
tjs: TableJsonSpec, | ||
additionalObjectPropertyColumns: AdditionalObjectPropertyColumn[], | ||
addOutputColumns: AdditionalOutputColumn[], | ||
pkCond: Nullable<ParentPrimaryKeyCondition>, | ||
@@ -161,14 +163,9 @@ orderBy: Nullable<string>, | ||
// Additional property column names must exist as projected column names in the base sql. | ||
const propCols = additionalObjectPropertyColumns.map(c => typeof(c) == 'string' ? c : c.property); | ||
const projectedPropNames = new Set(getPropertySelectEntries(baseSql).map(prop => prop.projectedName)); | ||
const invalidPropCols = propCols.filter(pc => !projectedPropNames.has(pc)); | ||
if (invalidPropCols.length > 0) | ||
throw new SpecError(specLoc, `Extracted property column(s) not found: ${invalidPropCols.join(', ')}.`); | ||
verifyTableFieldExpressionsValid(addOutputColumns, tjs.table, this.defaultSchema, this.dbmd, specLoc); | ||
return { | ||
...baseSql, | ||
objectWrapProperties: true, | ||
additionalObjectPropertyColumns | ||
}; | ||
const alias = baseSql.fromEntries[0].alias; | ||
const additionalOutputSelectEntries = | ||
this.tableFieldExpressionSelectEntries(addOutputColumns, tjs.table, alias, specLoc); | ||
return { ...baseSql, objectWrapProperties: true, additionalOutputSelectEntries }; | ||
} | ||
@@ -189,3 +186,3 @@ | ||
{ | ||
const baseSql = this.baseSql(tjs, childFkCond, orderBy, specLoc); | ||
const baseSql = this.baseSql(tjs, childFkCond, null, specLoc); | ||
@@ -198,4 +195,4 @@ if (unwrap && getPropertySelectEntries(baseSql).length != 1) | ||
aggregateToArray: true, | ||
objectWrapProperties: !unwrap, | ||
fromEntriesLeadingComment: `base query for table '${tjs.table}'`, | ||
aggregateOrderBy: orderBy, | ||
objectWrapProperties: !unwrap | ||
}; | ||
@@ -221,17 +218,18 @@ } | ||
( | ||
tjs: TableJsonSpec, | ||
fieldExpressions: Nullable<(string | TableFieldExpr)[]>, | ||
table: string, | ||
tableAlias: string, | ||
specLoc: SpecLocation | ||
) | ||
: SelectEntry[] | ||
: (FieldSelectEntry | ExpressionSelectEntry)[] | ||
{ | ||
verifyTableFieldExpressionsValid(tjs, this.defaultSchema, this.dbmd, specLoc); | ||
verifyTableFieldExpressionsValid(fieldExpressions, table, this.defaultSchema, this.dbmd, specLoc); | ||
if (!tjs.fieldExpressions) | ||
if (!fieldExpressions) | ||
return []; | ||
const dbFieldsByName: Map<string,Field> = this.getTableFieldsByName(tjs.table, specLoc); | ||
const dbFieldsByName: Map<string,Field> = this.getTableFieldsByName(table, specLoc); | ||
return tjs.fieldExpressions.map((tfe, ix) => { | ||
const feLoc = addLocPart(specLoc, `fieldExpressions entry #${ix+1} of table ${tjs.table}`); | ||
return fieldExpressions.map((tfe, ix) => { | ||
const feLoc = addLocPart(specLoc, `fieldExpressions entry #${ix+1} of table ${table}`); | ||
const projectedName = this.jsonPropertyName(tfe, feLoc); | ||
@@ -244,3 +242,3 @@ | ||
if (dbField == undefined) | ||
throw new SpecError(specLoc, `No metadata found for field ${tjs.table}.${fieldName}.`); | ||
throw new SpecError(specLoc, `No metadata found for field ${table}.${fieldName}.`); | ||
@@ -286,7 +284,7 @@ return { | ||
for (const parent of getInlineParentSpecs(tjs)) | ||
for (const parSpec of getInlineParentSpecs(tjs)) | ||
{ | ||
const ipLoc = addLocPart(specLoc, `parent table '${parent.table}'`); | ||
const ipLoc = addLocPart(specLoc, `parent table '${parSpec.table}'`); | ||
sqlParts.addParts(this.inlineParentSqlParts(parent, relId, alias, sqlParts.getAliases(), ipLoc)); | ||
sqlParts.addParts(this.inlineParentSqlParts(parSpec, relId, alias, sqlParts.getAliases(), ipLoc)); | ||
} | ||
@@ -297,3 +295,2 @@ | ||
// Return sql parts for the *including* (child) table's SQL query which are contributed by the inline parent table. | ||
private inlineParentSqlParts | ||
@@ -311,4 +308,4 @@ ( | ||
const subqueryAlias = parentSpec.subqueryAlias || makeNameNotInSet('q', avoidAliases); | ||
sqlParts.addAlias(subqueryAlias); | ||
const fromEntryAlias = parentSpec.fromEntryAlias || createTableAlias(parentSpec.table, avoidAliases); | ||
sqlParts.addAlias(fromEntryAlias); | ||
@@ -332,3 +329,3 @@ const parentPropsSql = this.baseSql(parentSpec, null, null, specLoc, 'export-pk-fields-hidden'); | ||
query: parentPropsSql, | ||
alias: subqueryAlias, | ||
alias: fromEntryAlias, | ||
join: { | ||
@@ -339,3 +336,3 @@ joinType: 'LEFT', | ||
fromAlias: childAlias, | ||
parentAlias: subqueryAlias, | ||
parentAlias: fromEntryAlias, | ||
matchedFields, | ||
@@ -355,3 +352,3 @@ matchMustExist | ||
projectedName: parentSelectEntry.projectedName, | ||
parentAlias: subqueryAlias, | ||
parentAlias: fromEntryAlias, | ||
parentTable: parentRelId, | ||
@@ -615,2 +612,3 @@ parentSelectEntry: parentSelectEntry, | ||
} | ||
} // QuerySQLSpecGenerator | ||
@@ -617,0 +615,0 @@ |
import {Field, ForeignKeyComponent, RelId} from "../dbmd"; | ||
import {AdditionalObjectPropertyColumn} from "../query-specs"; | ||
import {sorted} from "../util/collections"; | ||
@@ -15,4 +14,5 @@ import {Nullable} from "../util/mod"; | ||
objectWrapProperties?: Nullable<boolean>; | ||
additionalObjectPropertyColumns?: Nullable<AdditionalObjectPropertyColumn[]>; | ||
additionalOutputSelectEntries?: Nullable<AdditionalOutputSelectEntry[]>; | ||
aggregateToArray?: Nullable<boolean>; | ||
aggregateOrderBy?: Nullable<string>; | ||
selectEntriesLeadingComment?: Nullable<string>; | ||
@@ -39,2 +39,3 @@ fromEntriesLeadingComment?: Nullable<string>; | ||
readonly sourceCodeFieldType: Nullable<string | { [srcLang: string]: string }>; | ||
readonly comment?: Nullable<string>; | ||
} | ||
@@ -51,2 +52,3 @@ | ||
readonly sourceCodeFieldType: string | { [srcLang: string]: string }; | ||
readonly comment?: Nullable<string>; | ||
} | ||
@@ -90,4 +92,7 @@ | ||
readonly displayOrder?: undefined; | ||
readonly comment?: Nullable<string>; | ||
} | ||
export type AdditionalOutputSelectEntry = FieldSelectEntry | ExpressionSelectEntry; | ||
export type FromEntry = | ||
@@ -103,3 +108,3 @@ TableFromEntry | | ||
readonly join?: Nullable<Join>; | ||
readonly comment?: undefined; | ||
readonly comment?: Nullable<string>; | ||
} | ||
@@ -125,3 +130,4 @@ | ||
export type ParentChildCondition = | ||
(ParentPrimaryKeyCondition | ChildForeignKeyCondition ) & { readonly fromAlias: string }; | ||
(ParentPrimaryKeyCondition | ChildForeignKeyCondition) & | ||
{ readonly fromAlias: string }; // The FROM clause entry alias that the condition is applied on. | ||
@@ -228,3 +234,3 @@ export interface ParentPrimaryKeyCondition | ||
{ | ||
const alias = makeNameNotInSet(lowerCaseInitials(relName, '_') + '_', this.aliases); | ||
const alias = createTableAlias(relName, this.aliases); | ||
this.aliases.add(alias); | ||
@@ -272,2 +278,839 @@ return alias; | ||
else throw new Error(`No base table for sql: ${sql}`); | ||
} | ||
export const generalSqlKeywordsLowercase = new Set([ | ||
'a', | ||
'abort', | ||
'abs', | ||
'absolute', | ||
'access', | ||
'action', | ||
'ada', | ||
'add', | ||
'admin', | ||
'after', | ||
'aggregate', | ||
'alias', | ||
'all', | ||
'allocate', | ||
'also', | ||
'alter', | ||
'always', | ||
'analyse', | ||
'analyze', | ||
'and', | ||
'any', | ||
'are', | ||
'array', | ||
'as', | ||
'asc', | ||
'asensitive', | ||
'assertion', | ||
'assignment', | ||
'asymmetric', | ||
'at', | ||
'atomic', | ||
'attribute', | ||
'attributes', | ||
'audit', | ||
'authorization', | ||
'auto_increment', | ||
'avg', | ||
'avg_row_length', | ||
'backup', | ||
'backward', | ||
'before', | ||
'begin', | ||
'bernoulli', | ||
'between', | ||
'bigint', | ||
'binary', | ||
'bit', | ||
'bit_length', | ||
'bitvar', | ||
'blob', | ||
'bool', | ||
'boolean', | ||
'both', | ||
'breadth', | ||
'break', | ||
'browse', | ||
'bulk', | ||
'by', | ||
'c', | ||
'cache', | ||
'call', | ||
'called', | ||
'cardinality', | ||
'cascade', | ||
'cascaded', | ||
'case', | ||
'cast', | ||
'catalog', | ||
'catalog_name', | ||
'ceil', | ||
'ceiling', | ||
'chain', | ||
'change', | ||
'char', | ||
'char_length', | ||
'character', | ||
'character_length', | ||
'character_set_catalog', | ||
'character_set_name', | ||
'character_set_schema', | ||
'characteristics', | ||
'characters', | ||
'check', | ||
'checked', | ||
'checkpoint', | ||
'checksum', | ||
'class', | ||
'class_origin', | ||
'clob', | ||
'close', | ||
'cluster', | ||
'clustered', | ||
'coalesce', | ||
'cobol', | ||
'collate', | ||
'collation', | ||
'collation_catalog', | ||
'collation_name', | ||
'collation_schema', | ||
'collect', | ||
'column', | ||
'column_name', | ||
'columns', | ||
'command_function', | ||
'command_function_code', | ||
'comment', | ||
'commit', | ||
'committed', | ||
'completion', | ||
'compress', | ||
'compute', | ||
'condition', | ||
'condition_number', | ||
'connect', | ||
'connection', | ||
'connection_name', | ||
'constraint', | ||
'constraint_catalog', | ||
'constraint_name', | ||
'constraint_schema', | ||
'constraints', | ||
'constructor', | ||
'contains', | ||
'containstable', | ||
'continue', | ||
'conversion', | ||
'convert', | ||
'copy', | ||
'corr', | ||
'corresponding', | ||
'count', | ||
'covar_pop', | ||
'covar_samp', | ||
'create', | ||
'createdb', | ||
'createrole', | ||
'createuser', | ||
'cross', | ||
'csv', | ||
'cube', | ||
'cume_dist', | ||
'current', | ||
'current_date', | ||
'current_default_transform_group', | ||
'current_path', | ||
'current_role', | ||
'current_time', | ||
'current_timestamp', | ||
'current_transform_group_for_type', | ||
'current_user', | ||
'cursor', | ||
'cursor_name', | ||
'cycle', | ||
'data', | ||
'database', | ||
'databases', | ||
'date', | ||
'datetime', | ||
'datetime_interval_code', | ||
'datetime_interval_precision', | ||
'day', | ||
'day_hour', | ||
'day_microsecond', | ||
'day_minute', | ||
'day_second', | ||
'dayofmonth', | ||
'dayofweek', | ||
'dayofyear', | ||
'dbcc', | ||
'deallocate', | ||
'dec', | ||
'decimal', | ||
'declare', | ||
'default', | ||
'defaults', | ||
'deferrable', | ||
'deferred', | ||
'defined', | ||
'definer', | ||
'degree', | ||
'delay_key_write', | ||
'delayed', | ||
'delete', | ||
'delimiter', | ||
'delimiters', | ||
'dense_rank', | ||
'deny', | ||
'depth', | ||
'deref', | ||
'derived', | ||
'desc', | ||
'describe', | ||
'descriptor', | ||
'destroy', | ||
'destructor', | ||
'deterministic', | ||
'diagnostics', | ||
'dictionary', | ||
'disable', | ||
'disconnect', | ||
'disk', | ||
'dispatch', | ||
'distinct', | ||
'distinctrow', | ||
'distributed', | ||
'div', | ||
'do', | ||
'domain', | ||
'double', | ||
'drop', | ||
'dual', | ||
'dummy', | ||
'dump', | ||
'dynamic', | ||
'dynamic_function', | ||
'dynamic_function_code', | ||
'each', | ||
'element', | ||
'else', | ||
'elseif', | ||
'enable', | ||
'enclosed', | ||
'encoding', | ||
'encrypted', | ||
'end', | ||
'end-exec', | ||
'enum', | ||
'equals', | ||
'errlvl', | ||
'escape', | ||
'escaped', | ||
'every', | ||
'except', | ||
'exception', | ||
'exclude', | ||
'excluding', | ||
'exclusive', | ||
'exec', | ||
'execute', | ||
'existing', | ||
'exists', | ||
'exit', | ||
'exp', | ||
'explain', | ||
'external', | ||
'extract', | ||
'false', | ||
'fetch', | ||
'fields', | ||
'file', | ||
'fillfactor', | ||
'filter', | ||
'final', | ||
'first', | ||
'float', | ||
'float4', | ||
'float8', | ||
'floor', | ||
'flush', | ||
'following', | ||
'for', | ||
'force', | ||
'foreign', | ||
'fortran', | ||
'forward', | ||
'found', | ||
'free', | ||
'freetext', | ||
'freetexttable', | ||
'freeze', | ||
'from', | ||
'full', | ||
'fulltext', | ||
'function', | ||
'fusion', | ||
'g', | ||
'general', | ||
'generated', | ||
'get', | ||
'global', | ||
'go', | ||
'goto', | ||
'grant', | ||
'granted', | ||
'grants', | ||
'greatest', | ||
'group', | ||
'grouping', | ||
'handler', | ||
'having', | ||
'header', | ||
'heap', | ||
'hierarchy', | ||
'high_priority', | ||
'hold', | ||
'holdlock', | ||
'host', | ||
'hosts', | ||
'hour', | ||
'hour_microsecond', | ||
'hour_minute', | ||
'hour_second', | ||
'identified', | ||
'identity', | ||
'identity_insert', | ||
'identitycol', | ||
'if', | ||
'ignore', | ||
'ilike', | ||
'immediate', | ||
'immutable', | ||
'implementation', | ||
'implicit', | ||
'in', | ||
'include', | ||
'including', | ||
'increment', | ||
'index', | ||
'indicator', | ||
'infile', | ||
'infix', | ||
'inherit', | ||
'inherits', | ||
'initial', | ||
'initialize', | ||
'initially', | ||
'inner', | ||
'inout', | ||
'input', | ||
'insensitive', | ||
'insert', | ||
'insert_id', | ||
'instance', | ||
'instantiable', | ||
'instead', | ||
'int', | ||
'int1', | ||
'int2', | ||
'int3', | ||
'int4', | ||
'int8', | ||
'integer', | ||
'intersect', | ||
'intersection', | ||
'interval', | ||
'into', | ||
'invoker', | ||
'is', | ||
'isam', | ||
'isnull', | ||
'isolation', | ||
'iterate', | ||
'join', | ||
'k', | ||
'key', | ||
'key_member', | ||
'key_type', | ||
'keys', | ||
'kill', | ||
'lancompiler', | ||
'language', | ||
'large', | ||
'last', | ||
'last_insert_id', | ||
'lateral', | ||
'lead', | ||
'leading', | ||
'least', | ||
'leave', | ||
'left', | ||
'length', | ||
'less', | ||
'level', | ||
'like', | ||
'limit', | ||
'lineno', | ||
'lines', | ||
'listen', | ||
'ln', | ||
'load', | ||
'local', | ||
'localtime', | ||
'localtimestamp', | ||
'location', | ||
'locator', | ||
'lock', | ||
'login', | ||
'logs', | ||
'long', | ||
'longblob', | ||
'longtext', | ||
'loop', | ||
'low_priority', | ||
'lower', | ||
'm', | ||
'map', | ||
'match', | ||
'matched', | ||
'max', | ||
'max_rows', | ||
'maxextents', | ||
'maxvalue', | ||
'mediumblob', | ||
'mediumint', | ||
'mediumtext', | ||
'member', | ||
'merge', | ||
'message_length', | ||
'message_octet_length', | ||
'message_text', | ||
'method', | ||
'middleint', | ||
'min', | ||
'min_rows', | ||
'minus', | ||
'minute', | ||
'minute_microsecond', | ||
'minute_second', | ||
'minvalue', | ||
'mlslabel', | ||
'mod', | ||
'mode', | ||
'modifies', | ||
'modify', | ||
'module', | ||
'month', | ||
'monthname', | ||
'more', | ||
'move', | ||
'multiset', | ||
'mumps', | ||
'myisam', | ||
'name', | ||
'names', | ||
'national', | ||
'natural', | ||
'nchar', | ||
'nclob', | ||
'nesting', | ||
'new', | ||
'next', | ||
'no', | ||
'no_write_to_binlog', | ||
'noaudit', | ||
'nocheck', | ||
'nocompress', | ||
'nocreatedb', | ||
'nocreaterole', | ||
'nocreateuser', | ||
'noinherit', | ||
'nologin', | ||
'nonclustered', | ||
'none', | ||
'normalize', | ||
'normalized', | ||
'nosuperuser', | ||
'not', | ||
'nothing', | ||
'notify', | ||
'notnull', | ||
'nowait', | ||
'null', | ||
'nullable', | ||
'nullif', | ||
'nulls', | ||
'number', | ||
'numeric', | ||
'object', | ||
'octet_length', | ||
'octets', | ||
'of', | ||
'off', | ||
'offline', | ||
'offset', | ||
'offsets', | ||
'oids', | ||
'old', | ||
'on', | ||
'online', | ||
'only', | ||
'open', | ||
'opendatasource', | ||
'openquery', | ||
'openrowset', | ||
'openxml', | ||
'operation', | ||
'operator', | ||
'optimize', | ||
'option', | ||
'optionally', | ||
'options', | ||
'or', | ||
'order', | ||
'ordering', | ||
'ordinality', | ||
'others', | ||
'out', | ||
'outer', | ||
'outfile', | ||
'output', | ||
'over', | ||
'overlaps', | ||
'overlay', | ||
'overriding', | ||
'owner', | ||
'pack_keys', | ||
'pad', | ||
'parameter', | ||
'parameter_mode', | ||
'parameter_name', | ||
'parameter_ordinal_position', | ||
'parameter_specific_catalog', | ||
'parameter_specific_name', | ||
'parameter_specific_schema', | ||
'parameters', | ||
'partial', | ||
'partition', | ||
'pascal', | ||
'password', | ||
'path', | ||
'pctfree', | ||
'percent', | ||
'percent_rank', | ||
'percentile_cont', | ||
'percentile_disc', | ||
'placing', | ||
'plan', | ||
'pli', | ||
'position', | ||
'postfix', | ||
'power', | ||
'preceding', | ||
'precision', | ||
'prefix', | ||
'preorder', | ||
'prepare', | ||
'prepared', | ||
'preserve', | ||
'primary', | ||
'print', | ||
'prior', | ||
'privileges', | ||
'proc', | ||
'procedural', | ||
'procedure', | ||
'process', | ||
'processlist', | ||
'public', | ||
'purge', | ||
'quote', | ||
'raid0', | ||
'raiserror', | ||
'range', | ||
'rank', | ||
'raw', | ||
'read', | ||
'reads', | ||
'readtext', | ||
'real', | ||
'recheck', | ||
'reconfigure', | ||
'recursive', | ||
'ref', | ||
'references', | ||
'referencing', | ||
'regexp', | ||
'regr_avgx', | ||
'regr_avgy', | ||
'regr_count', | ||
'regr_intercept', | ||
'regr_r2', | ||
'regr_slope', | ||
'regr_sxx', | ||
'regr_sxy', | ||
'regr_syy', | ||
'reindex', | ||
'relative', | ||
'release', | ||
'reload', | ||
'rename', | ||
'repeat', | ||
'repeatable', | ||
'replace', | ||
'replication', | ||
'require', | ||
'reset', | ||
'resignal', | ||
'resource', | ||
'restart', | ||
'restore', | ||
'restrict', | ||
'result', | ||
'return', | ||
'returned_cardinality', | ||
'returned_length', | ||
'returned_octet_length', | ||
'returned_sqlstate', | ||
'returns', | ||
'revoke', | ||
'right', | ||
'rlike', | ||
'role', | ||
'rollback', | ||
'rollup', | ||
'routine', | ||
'routine_catalog', | ||
'routine_name', | ||
'routine_schema', | ||
'row', | ||
'row_count', | ||
'row_number', | ||
'rowcount', | ||
'rowguidcol', | ||
'rowid', | ||
'rownum', | ||
'rows', | ||
'rule', | ||
'save', | ||
'savepoint', | ||
'scale', | ||
'schema', | ||
'schema_name', | ||
'schemas', | ||
'scope', | ||
'scope_catalog', | ||
'scope_name', | ||
'scope_schema', | ||
'scroll', | ||
'search', | ||
'second', | ||
'second_microsecond', | ||
'section', | ||
'security', | ||
'select', | ||
'self', | ||
'sensitive', | ||
'separator', | ||
'sequence', | ||
'serializable', | ||
'server_name', | ||
'session', | ||
'session_user', | ||
'set', | ||
'setof', | ||
'sets', | ||
'setuser', | ||
'share', | ||
'show', | ||
'shutdown', | ||
'signal', | ||
'similar', | ||
'simple', | ||
'size', | ||
'smallint', | ||
'some', | ||
'soname', | ||
'source', | ||
'space', | ||
'spatial', | ||
'specific', | ||
'specific_name', | ||
'specifictype', | ||
'sql', | ||
'sql_big_result', | ||
'sql_big_selects', | ||
'sql_big_tables', | ||
'sql_calc_found_rows', | ||
'sql_log_off', | ||
'sql_log_update', | ||
'sql_low_priority_updates', | ||
'sql_select_limit', | ||
'sql_small_result', | ||
'sql_warnings', | ||
'sqlca', | ||
'sqlcode', | ||
'sqlerror', | ||
'sqlexception', | ||
'sqlstate', | ||
'sqlwarning', | ||
'sqrt', | ||
'ssl', | ||
'stable', | ||
'start', | ||
'starting', | ||
'state', | ||
'statement', | ||
'static', | ||
'statistics', | ||
'status', | ||
'stddev_pop', | ||
'stddev_samp', | ||
'stdin', | ||
'stdout', | ||
'storage', | ||
'straight_join', | ||
'strict', | ||
'string', | ||
'structure', | ||
'style', | ||
'subclass_origin', | ||
'sublist', | ||
'submultiset', | ||
'substring', | ||
'successful', | ||
'sum', | ||
'superuser', | ||
'symmetric', | ||
'synonym', | ||
'sysdate', | ||
'sysid', | ||
'system', | ||
'system_user', | ||
'table', | ||
'table_name', | ||
'tables', | ||
'tablesample', | ||
'tablespace', | ||
'temp', | ||
'template', | ||
'temporary', | ||
'terminate', | ||
'terminated', | ||
'text', | ||
'textsize', | ||
'than', | ||
'then', | ||
'ties', | ||
'time', | ||
'timestamp', | ||
'timezone_hour', | ||
'timezone_minute', | ||
'tinyblob', | ||
'tinyint', | ||
'tinytext', | ||
'to', | ||
'toast', | ||
'top', | ||
'top_level_count', | ||
'trailing', | ||
'tran', | ||
'transaction', | ||
'transaction_active', | ||
'transactions_committed', | ||
'transactions_rolled_back', | ||
'transform', | ||
'transforms', | ||
'translate', | ||
'translation', | ||
'treat', | ||
'trigger', | ||
'trigger_catalog', | ||
'trigger_name', | ||
'trigger_schema', | ||
'trim', | ||
'true', | ||
'truncate', | ||
'trusted', | ||
'tsequal', | ||
'type', | ||
'uescape', | ||
'uid', | ||
'unbounded', | ||
'uncommitted', | ||
'under', | ||
'undo', | ||
'unencrypted', | ||
'union', | ||
'unique', | ||
'unknown', | ||
'unlisten', | ||
'unlock', | ||
'unnamed', | ||
'unnest', | ||
'unsigned', | ||
'until', | ||
'update', | ||
'updatetext', | ||
'upper', | ||
'usage', | ||
'use', | ||
'user', | ||
'user_defined_type_catalog', | ||
'user_defined_type_code', | ||
'user_defined_type_name', | ||
'user_defined_type_schema', | ||
'using', | ||
'utc_date', | ||
'utc_time', | ||
'utc_timestamp', | ||
'vacuum', | ||
'valid', | ||
'validate', | ||
'validator', | ||
'value', | ||
'values', | ||
'var_pop', | ||
'var_samp', | ||
'varbinary', | ||
'varchar', | ||
'varchar2', | ||
'varcharacter', | ||
'variable', | ||
'variables', | ||
'varying', | ||
'verbose', | ||
'view', | ||
'volatile', | ||
'waitfor', | ||
'when', | ||
'whenever', | ||
'where', | ||
'while', | ||
'width_bucket', | ||
'window', | ||
'with', | ||
'within', | ||
'without', | ||
'work', | ||
'write', | ||
'writetext', | ||
'x509', | ||
'xor', | ||
'year', | ||
'year_month', | ||
'zerofill', | ||
'zone', | ||
]); | ||
export function createTableAlias(relName: string, avoid: Set<string>): string | ||
{ | ||
let alias = makeNameNotInSet(lowerCaseInitials(relName, '_'), avoid); | ||
if (alias.length > 1 && generalSqlKeywordsLowercase.has(alias.toLowerCase())) | ||
alias = alias + "_"; | ||
return alias; | ||
} |
@@ -210,5 +210,5 @@ export function computeIfAbsent<K,V> | ||
export function isNonEmpty<T>(ts: T[] | null | undefined): ts is NonNullable<T[]> | ||
export function nonEmpty<T>(ts: T[] | null | undefined): ts is NonNullable<T[]> | ||
{ | ||
return (ts != null && ts.length > 0); | ||
} |
@@ -1,4 +0,4 @@ | ||
import { CaseSensitivity } from '../dbmd/database-metadata'; | ||
import { PropertyNameDefault } from '../query-specs'; | ||
import { lowerCamelCase, snakeCase, upperCamelCase } from './strings'; | ||
import {CaseSensitivity} from '../dbmd/database-metadata'; | ||
import {PropertyNameDefault} from '../query-specs'; | ||
import {lowerCamelCase, snakeCase, upperCamelCase} from './strings'; | ||
@@ -5,0 +5,0 @@ export function propertyNameDefaultFunction |
@@ -12,5 +12,9 @@ export function upperCamelCase(name: string): string | ||
{ | ||
const cleanedName = | ||
name.replaceAll(/[._ ]/g,'_') | ||
.replaceAll(/[^a-zA-Z0-9_]/g, ""); | ||
const parts: string[] = []; | ||
for ( const word of name.split(sep) ) | ||
for ( const word of cleanedName.split(sep) ) | ||
{ | ||
@@ -91,2 +95,21 @@ if (word.length > 0) | ||
export function makeNameNotInSets | ||
( | ||
baseName: string, | ||
avoidSets: Set<string>[], | ||
suffixSep: string = "" | ||
) | ||
: string | ||
{ | ||
const nameOk = (name: string) => !avoidSets.some(set => set.has(name)); | ||
if (nameOk(baseName)) | ||
return baseName; | ||
else | ||
{ | ||
let i = 1; | ||
while (!nameOk(baseName + suffixSep + i)) ++i; | ||
return baseName + suffixSep + i; | ||
} | ||
} | ||
export function indentLines | ||
@@ -93,0 +116,0 @@ ( |
@@ -7,3 +7,3 @@ { | ||
"target": "ES2018", | ||
"lib": ["ES2020"], | ||
"lib": ["ES2021"], | ||
"module": "commonjs", | ||
@@ -10,0 +10,0 @@ "esModuleInterop": true, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
631085
11999