Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 60 additions & 4 deletions src/views/results/codegen.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ColumnMetaData, QueryResult } from "@ibm/mapepire-js";

export function queryResultToRpgDs(result: QueryResult<any>, source: string = 'Name') : string {
export function queryResultToRpgDs(result: QueryResult<any>, source: string = 'Name'): string {
let content = `dcl-ds row_t qualified template;\n`;
for (let i = 0; i < result.metadata.column_count; i++) {
const name = columnToRpgFieldName(result.metadata.columns[i], source);
Expand All @@ -10,7 +10,7 @@ export function queryResultToRpgDs(result: QueryResult<any>, source: string = 'N
return content;
}

export function columnToRpgFieldName(column: ColumnMetaData, source: string = 'Name') : string {
export function columnToRpgFieldName(column: ColumnMetaData, source: string = 'Name'): string {
let name = source === 'Label' ? column.label.toLowerCase().trim() : column.name.toLowerCase().trim();
name = name.replace(/\u00fc/g, "u") // ü -> u
.replace(/\u00e4/g, "a") // ä -> a
Expand All @@ -24,14 +24,14 @@ export function columnToRpgFieldName(column: ColumnMetaData, source: string = 'N
.replace(/\s+/g, "_") // remaining whitespaces to underscore
.replace(/[^a-zA-Z0-9_]/g, "") // remove non-alphanumeric chars
.replace(/\_+/i, "_") // replace multiple underscores with single underscore
.trim();
.trim();
if (!isNaN(+name.charAt(0))) {
name = `col` + name;
}
return name;
}

export function columnToRpgDefinition(column: ColumnMetaData) : string {
export function columnToRpgDefinition(column: ColumnMetaData): string {
switch (column.type) {
case `NUMERIC`:
return `zoned(${column.precision}${column.scale > 0 ? ' : ' + column.scale : ''})`;
Expand Down Expand Up @@ -59,3 +59,59 @@ export function columnToRpgDefinition(column: ColumnMetaData) : string {
return `// type:${column.type} precision:${column.precision} scale:${column.scale}`;
}
}

export function queryResultToUdtf(result: QueryResult<any>, sqlStatement: string): string {
let columnDefinitions = '';
for (let i = 0; i < result.metadata.column_count; i++) {
const column = result.metadata.columns[i];
columnDefinitions += ` ${column.name} ${columnToSqlDefinition(column)}`;
if (i < result.metadata.column_count - 1) {
columnDefinitions += ',\n';
} else {
columnDefinitions += '\n';
}
}

return `CREATE OR REPLACE FUNCTION MyFunction()\n`
+ ` RETURNS TABLE (\n`
+ columnDefinitions
+ ` )\n`
+ ` NOT DETERMINISTIC\n`
+ ` NO EXTERNAL ACTION\n`
+ ` READS SQL DATA\n`
+ ` SET OPTION COMMIT = *NONE,\n`
+ ` DYNUSRPRF = *USER,\n`
+ ` USRPRF = *USER\n`
+ ` BEGIN\n`
+ ` RETURN ${sqlStatement};\n`
+ ` END;`;
}

export function columnToSqlDefinition(column: ColumnMetaData): string {
switch (column.type) {
case 'NUMERIC':
return `NUMERIC(${column.precision},${column.scale})`;
case 'DECIMAL':
return `DECIMAL(${column.precision},${column.scale})`;
case 'CHAR':
return `CHAR(${column.precision})`;
case 'VARCHAR':
return `VARCHAR(${column.precision})`;
case 'DATE':
return `DATE`;
case 'TIME':
return `TIME`;
case 'TIMESTAMP':
return `TIMESTAMP`;
case 'SMALLINT':
return `INTEGER`;
case 'INTEGER':
return `INTEGER`;
case 'BIGINT':
return `BIGINT`;
case 'BOOLEAN':
return `BOOLEAN`;
default:
return `-- type:${column.type} precision:${column.precision} scale:${column.scale} */`;
}
}
26 changes: 23 additions & 3 deletions src/views/results/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Statement from "../../language/sql/statement";
import { ObjectRef, ParsedEmbeddedStatement, StatementGroup, StatementType } from "../../language/sql/types";
import { updateStatusBar } from "../jobManager/statusBar";
import { getLiteralsFromStatement, getPriorBindableStatement } from "./binding";
import { queryResultToRpgDs } from "./codegen";
import { queryResultToRpgDs, queryResultToUdtf } from "./codegen";
import { registerRunStatement } from "./editorUi";
import { generateSqlForAdvisedIndexes } from "./explain/advice";
import { DoveNodeView, PropertyNode } from "./explain/doveNodeView";
Expand All @@ -22,7 +22,7 @@ import { DoveTreeDecorationProvider } from "./explain/doveTreeDecorationProvider
import { ExplainTree } from "./explain/nodes";
import { ResultSetPanelProvider, SqlParameter } from "./resultSetPanelProvider";

export type StatementQualifier = "statement" | "bind" | "update" | "explain" | "onlyexplain" | "json" | "csv" | "cl" | "sql" | "rpg";
export type StatementQualifier = "statement" | "bind" | "update" | "explain" | "onlyexplain" | "json" | "csv" | "cl" | "sql" | "rpg" | "udtf";

export interface StatementInfo {
content: string,
Expand Down Expand Up @@ -440,6 +440,26 @@ async function runHandler(options?: StatementInfo) {
await vscode.window.showTextDocument(textDoc);
chosenView.setLoadingText(`RPG data structure generated.`, false);
}

} else if (statementDetail.qualifier === `udtf`) {
if (statementDetail.statement.type !== StatementType.Select) {
vscode.window.showErrorMessage('UDTF qualifier only supported for select statements');
} else {
chosenView.setLoadingText(`Executing SQL statement...`, false);
setCancelButtonVisibility(true);
updateStatusBar({executing: true});
const result = await JobManager.runSQLVerbose(statementDetail.content, undefined, 1);
setCancelButtonVisibility(false);
updateStatusBar({executing: false});
let content = `-- statement:\n`
+ `-- ${statementDetail.content.replace(/(\r\n|\r|\n)/g, '\n-- ') }\n\n`
+ `-- User-defined table function\n`
+ queryResultToUdtf(result, statementDetail.content);

const textDoc = await vscode.workspace.openTextDocument({ language: 'sql', content });
await vscode.window.showTextDocument(textDoc);
chosenView.setLoadingText(`User-defined table function generated.`, false);
}

} else {
// Otherwise... it's a bit complicated.
Expand Down Expand Up @@ -592,7 +612,7 @@ export function parseStatement(editor?: vscode.TextEditor, existingInfo?: Statem
}

if (statementInfo.content) {
[`cl`, `json`, `csv`, `sql`, `explain`, `update`, `rpg`, `bind`].forEach(mode => {
[`cl`, `json`, `csv`, `sql`, `explain`, `update`, `rpg`, `udtf`, `bind`].forEach(mode => {
if (statementInfo.content.trim().toLowerCase().startsWith(mode + `:`)) {
statementInfo.content = statementInfo.content.substring(mode.length + 1).trim();

Expand Down
Loading