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
66 changes: 57 additions & 9 deletions src/notebooks/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,69 @@ export class IBMiController {
let content = cell.document.getText().trim();
let chartDetail: ChartDetail | undefined;

({ chartDetail, content } = getStatementDetail(content, eol));
// Parse bind parameters BEFORE any other processing
let parameters: any[] | undefined;
const bindMatch = content.match(/bind:\s*\((.*?)\)\s*;?\s*$/s);
if (bindMatch) {
// Remove the bind statement from content
content = content.substring(0, bindMatch.index).trim();

// Parse the parameters
try {
const paramString = bindMatch[1];
// Simple parsing: split by comma, handle strings and numbers
parameters = paramString.split(',').map(p => {
p = p.trim();
// String literal
if ((p.startsWith("'") && p.endsWith("'")) || (p.startsWith('"') && p.endsWith('"'))) {
return p.substring(1, p.length - 1);
}
// Number
if (!isNaN(Number(p))) {
return Number(p);
}
// Null
if (p.toLowerCase() === 'null') {
return null;
}
// Default to string
return p;
});
} catch (e) {
items.push(vscode.NotebookCellOutputItem.stderr(`Failed to parse bind parameters: ${e.message}`));
break;
}
}

// For CREATE statements, keep the content as-is (they need semicolons)
// For other statements, use getStatementDetail to handle chart settings
const isCreateStatement = content.trim().toUpperCase().startsWith('CREATE');

if (!isCreateStatement) {
({ chartDetail, content } = getStatementDetail(content, eol));
}

// Execute the query
const query = selected.job.query(content);
const query = selected.job.query(content, parameters ? { parameters } : undefined);
const results = await query.execute(1000);

const table = results.data;
const columnNames = results.metadata.columns.map(c => c.name);
const columnNames = results.metadata?.columns?.map(c => c.name) || [];

// Check for OUT/INOUT parameters and display them
if (results.output_parms && results.output_parms.length > 0) {
const paramOutput = results.output_parms.map((param, index) => {
const paramName = param.name ? `**${param.name}**` : `**Parameter ${index + 1}**`;
const paramValue = param.value !== undefined && param.value !== null ? param.value : '-';
return `${paramName}: \`${paramValue}\``;
}).join('\n\n');

items.push(vscode.NotebookCellOutputItem.text(`### Output Parameters\n\n${paramOutput}`, `text/markdown`));
}

if (table === undefined && results.success && !results.has_results) {
items.push(vscode.NotebookCellOutputItem.text(`Statement executed successfully. ${results.update_count ? `${results.update_count} rows affected.` : ``}`, `text/markdown`));
break;
}

if (table.length > 0) {
} else if (table && table.length > 0) {
// Add `-` for blanks.
table.forEach(row => {
columnNames.forEach(key => {
Expand All @@ -104,7 +152,7 @@ export class IBMiController {

let fallbackToTable = true;

if (chartDetail.type) {
if (chartDetail && chartDetail.type) {
if (chartJsTypes.includes(chartDetail.type as ChartJsType)) {
const possibleChart = generateChart(execution.executionOrder, chartDetail, columnNames, table, generateChartHTMLCell);
if (possibleChart) {
Expand All @@ -117,7 +165,7 @@ export class IBMiController {
if (fallbackToTable) {
items.push(vscode.NotebookCellOutputItem.text(mdTable(table, columnNames), `text/markdown`));
}
} else {
} else if (table && table.length === 0 && results.has_results) {
items.push(vscode.NotebookCellOutputItem.stderr(`No rows returned from statement.`));
}

Expand Down
49 changes: 49 additions & 0 deletions src/views/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,55 @@ export const ServiceInfoLabel = `IBM i (SQL) Services`;

export const Examples: SQLExamplesList = {
"Notebooks": [
{
name: "Bind Parameters with OUT Parameters",
content: [
`## `.concat([
`This notebook demonstrates how to use bind parameters with stored procedures that have OUT or INOUT parameters.`,
`Use the \`bind:()\` syntax at the end of a CALL statement to pass parameter values.`,
`OUT and INOUT parameters will be displayed after execution.`,
].join('\n\n')),

`## Create a simple procedure with OUT parameter`,
[
`CREATE OR REPLACE PROCEDURE MYLIB.GREET_USER (`,
` IN USER_NAME VARCHAR(50),`,
` OUT GREETING VARCHAR(100)`,
`)`,
`LANGUAGE SQL`,
`BEGIN`,
` SET GREETING = 'Hello, ' || USER_NAME || '! Welcome to IBM i.';`,
`END`,
].join('\n'),

`## Call the procedure with bind parameters`,
[
`CALL MYLIB.GREET_USER(?, ?);`,
`bind:('Alice', ' ');`,
].join('\n'),

`## Create a procedure with multiple OUT parameters`,
[
`CREATE OR REPLACE PROCEDURE MYLIB.CALCULATE_VALUES (`,
` IN INPUT_NUM INTEGER,`,
` OUT DOUBLED INTEGER,`,
` OUT SQUARED INTEGER`,
`)`,
`LANGUAGE SQL`,
`BEGIN`,
` SET DOUBLED = INPUT_NUM * 2;`,
` SET SQUARED = INPUT_NUM * INPUT_NUM;`,
`END`,
].join('\n'),

`## Call with multiple OUT parameters`,
[
`CALL MYLIB.CALCULATE_VALUES(?, ?, ?);`,
`bind:(5, 0, 0);`,
].join('\n'),
],
isNotebook: true
},
{
name: "IBM i History",
content: [
Expand Down
Loading