From afe03795a6405d26b3ba807216e3d67855a85b49 Mon Sep 17 00:00:00 2001 From: DF Date: Mon, 9 Jun 2025 13:00:54 -0400 Subject: [PATCH 1/4] Handle update of null fields --- src/views/html.ts | 17 +++++++++++++++++ src/views/results/html.ts | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/views/html.ts b/src/views/html.ts index 9467df8c..800c0989 100644 --- a/src/views/html.ts +++ b/src/views/html.ts @@ -41,6 +41,23 @@ export function getHeader(options: {withCollapsed?: boolean} = {}): string { #resultset tbody tr { border-bottom: 1px solid var(--vscode-activityBar-border); + position: relative; + } + + #resultset tbody [contenteditable="true"].nullable:before { + color: var(--vscode-banner-foreground); + position: absolute; + top: -22px; + content: "Shift+Enter for null"; + background-color: var(--vscode-list-hoverBackground); + opacity: 1; + padding: 2px; + border: 1px solid var(--vscode-banner-foreground); + } + + #resultset tbody .null { + font-style: italic; + background-color: var(--vscode-editor-wordHighlightBackground); } ${options.withCollapsed ? /*css*/` diff --git a/src/views/results/html.ts b/src/views/results/html.ts index 1f842769..56d5ba55 100644 --- a/src/views/results/html.ts +++ b/src/views/results/html.ts @@ -44,6 +44,7 @@ export interface BasicColumn { name: string; useInWhere: boolean jsType: "number"|"asString"; + isNullable: boolean, maxInputLength?: number; } @@ -137,19 +138,14 @@ document.getElementById('resultset').onclick = function(e){ } // Can return undefined or {updateStatement, bindings, saneStatement?} - const getSqlStatement = (newValue, withSane = false) => { + const getSqlStatement = (newValue, withSane = false, nullify = false) => { const useRrn = updateKeyColumns.length === 1 && updateKeyColumns.some(col => col.name === 'RRN'); let bindings = []; let updateStatement = 'UPDATE ' + updateTable.table + ' t SET t.' + chosenColumn + ' = '; - if (chosenColumnDetail.maxInputLength >= 4 && newValue === 'null') { - // If the column can fit 'null', then set it to null value + if (nullify) { updateStatement += 'NULL'; - } else if (chosenColumnDetail.maxInputLength < 4 && newValue === '-') { - // If the column cannot fit 'null', then '-' is the null value - updateStatement += 'NULL'; - } else { switch (chosenColumnDetail.jsType) { case 'number': @@ -223,7 +219,11 @@ document.getElementById('resultset').onclick = function(e){ // Already editable, just return if (editableNode.contentEditable === 'true') return; + let nullify = false; editableNode.contentEditable = true; + if((editableNode.classList.contains("null") || editableNode.parentNode.classList.contains("null")) && editableNode.innerText === 'null') { + editableNode.innerText = ''; + } editableNode.focus(); updateMessageWithSql(originalValue); @@ -237,6 +237,9 @@ document.getElementById('resultset').onclick = function(e){ switch (e.key) { case 'Enter': + if(chosenColumnDetail.isNullable && e.shiftKey) { + nullify = true; + } e.preventDefault(); editableNode.blur(); break; @@ -263,19 +266,31 @@ document.getElementById('resultset').onclick = function(e){ editableNode.contentEditable = false; let newValue = editableNode.innerText; - if (newValue === originalValue) return; + if (!nullify && newValue === originalValue) return; if (chosenColumnDetail.maxInputLength && newValue.length > chosenColumnDetail.maxInputLength) { newValue = newValue.substring(0, chosenColumnDetail.maxInputLength); editableNode.innerText = newValue; } - const sql = getSqlStatement(newValue); + const sql = getSqlStatement(newValue, false, nullify); if (!sql) { editableNode.innerHTML = originalValue; return; } + if(nullify) { + editableNode.innerHTML = "null"; + if(editableNode.tagName.toLowerCase() === "td") { + editableNode.classList.add("null"); + } else { + editableNode.parentNode.classList.add("null"); + } + } else { + editableNode.classList.remove("null"); + editableNode.parentNode.classList.remove("null"); + } + requestCellUpdate(editableNode, originalValue, sql.updateStatement, sql.bindings); editableNode = undefined; @@ -481,6 +496,10 @@ export function generateScroller(basicSelect: string, isCL: boolean, withCancel? let newDiv = document.createElement("div"); newDiv.className = "hoverable"; + if(columnMetaData[currentColumn].nullable === 1) { + newCell.classList.add("nullable"); + newDiv.classList.add("nullable"); + } // Append a formatted JSON object to the cell const contentMightBeJson = typeof cell === 'string' && (cell.startsWith('{') || cell.startsWith('[')) && (cell.endsWith('}') || cell.endsWith(']')); @@ -501,7 +520,7 @@ export function generateScroller(basicSelect: string, isCL: boolean, withCancel? newDiv.style["font-family"] = "monospace"; newDiv.appendChild(document.createTextNode(cell === undefined ? 'null' : cell)); if(cell === undefined || cell === null) { - newDiv.style["font-style"] = "italic"; + newCell.classList.add("null"); } } From 3fd30c2bb07a694be2b818e7edd98defb633b0b2 Mon Sep 17 00:00:00 2001 From: DF Date: Mon, 9 Jun 2025 14:10:36 -0400 Subject: [PATCH 2/4] Remove italic for nullify tooltip if field is already null --- src/views/html.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/html.ts b/src/views/html.ts index 800c0989..3d221726 100644 --- a/src/views/html.ts +++ b/src/views/html.ts @@ -52,6 +52,7 @@ export function getHeader(options: {withCollapsed?: boolean} = {}): string { background-color: var(--vscode-list-hoverBackground); opacity: 1; padding: 2px; + font-style: normal; border: 1px solid var(--vscode-banner-foreground); } From 5ce424211b667f4ac3b225e5977cbd354ff45949 Mon Sep 17 00:00:00 2001 From: DF Date: Mon, 9 Jun 2025 14:29:54 -0400 Subject: [PATCH 3/4] Add missing resultSetPanelProvider.ts file change --- src/views/results/resultSetPanelProvider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/results/resultSetPanelProvider.ts b/src/views/results/resultSetPanelProvider.ts index 13264867..0e6f0625 100644 --- a/src/views/results/resultSetPanelProvider.ts +++ b/src/views/results/resultSetPanelProvider.ts @@ -230,6 +230,7 @@ export class ResultSetPanelProvider implements WebviewViewProvider { name: column.COLUMN_NAME, jsType: column.NUMERIC_PRECISION ? `number` : `asString`, useInWhere: column.IS_IDENTITY === `YES`, + isNullable: column.IS_NULLABLE === `Y`, maxInputLength: column.CHARACTER_MAXIMUM_LENGTH })); @@ -263,7 +264,7 @@ export class ResultSetPanelProvider implements WebviewViewProvider { basicSelect = `select rrn(${cName}) as RRN, ${possibleColumnList} from ${schema}.${ref.object.name} as ${cName} ${fromWhereClause || ``}`; - currentColumns = [{ name: `RRN`, jsType: `number`, useInWhere: true }, ...currentColumns]; + currentColumns = [{ name: `RRN`, jsType: `number`, isNullable: false, useInWhere: true }, ...currentColumns]; } updatable = { From 4fe575546bca5e58cd24812011bf682d3ad14426 Mon Sep 17 00:00:00 2001 From: DF Date: Mon, 23 Jun 2025 15:53:14 -0400 Subject: [PATCH 4/4] Make column width adjustable --- package.json | 3 +- src/views/html.ts | 97 +++++++++------ src/views/jobManager/jobLog.ts | 52 ++++----- src/views/results/contributes.json | 3 +- src/views/results/html.ts | 182 ++++++++++++++++++++--------- 5 files changed, 215 insertions(+), 122 deletions(-) diff --git a/package.json b/package.json index d5a26d6f..7ba97b3b 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,8 @@ "default": "Name", "enum": [ "Name", - "Label" + "Label", + "Both" ], "enumDescriptions": [ "Show the column name", diff --git a/src/views/html.ts b/src/views/html.ts index 3d221726..73ad4cb7 100644 --- a/src/views/html.ts +++ b/src/views/html.ts @@ -6,45 +6,78 @@ export function getHeader(options: {withCollapsed?: boolean} = {}): string {