Skip to content

Commit

Permalink
results: remove duplicated columns (#135)
Browse files Browse the repository at this point in the history
Removes duplicated columns from results by using the "array" mode in the PG client.
  • Loading branch information
joacoc authored Nov 14, 2023
1 parent 7afdb79 commit f2338d4
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 103 deletions.
26 changes: 20 additions & 6 deletions src/clients/sql.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Pool, PoolClient, PoolConfig, QueryResult } from "pg";
import { Pool, PoolClient, PoolConfig, QueryArrayResult, QueryResult } from "pg";
import AdminClient from "./admin";
import CloudClient from "./cloud";
import { Profile } from "../context/config";
Expand Down Expand Up @@ -116,11 +116,12 @@ export default class SqlClient {
* Internal queries are intended for exploring cases.
* Like quering the catalog, or information about Materialize.
* Queries goes to the pool, and no client is kept.
*
* @param statement
* @param values
* @returns query results
*/
async internalQuery(statement: string, values?: Array<any>): Promise<QueryResult<any>> {
async internalQuery(statement: string, values?: Array<any>): Promise<QueryArrayResult<any>> {
const pool = await this.pool;
const results = await pool.query(statement, values);

Expand All @@ -129,15 +130,28 @@ export default class SqlClient {


/**
* Private queries are intended for the user. A private query reuses always the same client.
* In this way, it functions like a shell, processing one statement after another.
* Private queries are intended for the user.
* A private query reuses always the same client.
* In this way, it functions like a shell,
* processing one statement after another.
*
* Another important difference is that
* it returns the values in Array mode.
*
* @param statement
* @param values
* @returns query results
*/
async privateQuery(statement: string, values?: Array<any>): Promise<QueryResult<any>> {
async privateQuery(statement: string, values?: Array<any>): Promise<QueryArrayResult<any>> {
const client = await this.privateClient;
const results = await client.query(statement, values);
// Row mode is a must.
// Otherwise when two columns have the same name, one is dropped
// Issue: https://github.com/brianc/node-postgres/issues/280
const results = await client.query({
rowMode: "array",
text: statement,
values
});

return results;
}
Expand Down
12 changes: 8 additions & 4 deletions src/context/asyncContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Errors, ExtensionError } from "../utilities/error";
import AppPassword from "./appPassword";
import { ActivityLogTreeProvider, AuthProvider, DatabaseTreeProvider, ResultsProvider } from "../providers";
import * as vscode from 'vscode';
import { QueryResult } from "pg";
import { QueryArrayResult, QueryResult } from "pg";
import { ExecuteCommandParseStatement } from "../clients/lsp";

/**
Expand Down Expand Up @@ -389,7 +389,7 @@ export default class AsyncContext extends Context {
* @param vals
* @returns query results
*/
async internalQuery(text: string, vals?: Array<any>) {
async internalQuery(text: string, vals?: Array<any>): Promise<QueryResult<any>> {
const client = await this.getSqlClient();

return await client.internalQuery(text, vals);
Expand All @@ -398,13 +398,17 @@ export default class AsyncContext extends Context {
/**
* Private queries are intended for the user.
* A private query reuses always the same client.
* In this way, it functions like a shell, processing one statement after another.
* In this way, it functions like a shell,
* processing one statement after another.
*
* Another important difference is that
* it returns the values in Array mode.
*
* @param text
* @param vals
* @returns query results
*/
async privateQuery(text: string, vals?: Array<any>) {
async privateQuery(text: string, vals?: Array<any>): Promise<QueryArrayResult<any>> {
const client = await this.getSqlClient();

return await client.privateQuery(text, vals);
Expand Down
89 changes: 0 additions & 89 deletions src/providers/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,95 +2,6 @@ import * as vscode from 'vscode';
import { randomUUID } from 'crypto';
import AsyncContext from "../context/asyncContext";

// vscode.commands.executeCommand('queryResults.focus').then(async () => {
// const document = activeEditor.document;
// const selection = activeEditor.selection;
// const textSelected = activeEditor.document.getText(selection).trim();
// const query = textSelected ? textSelected : document.getText();
// const fileName = document.fileName;

// // Identify the query to not overlap results.
// // When a user press many times the run query button
// // the results from one query can overlap the results
// // from another. We only want to display the last results.
// const id = randomUUID();

// try {
// // Clean the results by emitting a newQuery event.
// context.emit("event", { type: EventType.newQuery, data: { id } });

// try {
// const statements = await context.parseSql(query);

// console.log("[RunSQLCommand]", "Running statements: ", statements);

// const lastStatement = statements[statements.length - 1];
// for (const statement of statements) {
// console.log("[RunSQLCommand]", "Running statement: ", statement);

// // Benchmark
// const startTime = Date.now();
// try {
// const results = await context.privateQuery(statement.sql);
// const endTime = Date.now();
// const elapsedTime = endTime - startTime;

// console.log("[RunSQLCommand]", "Results: ", results);
// console.log("[RunSQLCommand]", "Emitting results.");

// // Only display the results from the last statement.
// if (lastStatement === statement) {
// if (Array.isArray(results)) {
// context.emit("event", { type: EventType.queryResults, data: { ...results[0], elapsedTime, id } });
// } else {
// context.emit("event", { type: EventType.queryResults, data: { ...results, elapsedTime, id } });
// }
// }
// activityLogProvider.addLog({
// status: "success",
// latency: elapsedTime, // assuming elapsedTime holds the time taken for the query to execute
// sql: statement.sql
// });
// } catch (error: any) {
// console.log("[RunSQLCommand]", error.toString());
// console.log("[RunSQLCommand]", JSON.stringify(error));
// const endTime = Date.now();
// const elapsedTime = endTime - startTime;

// activityLogProvider.addLog({
// status: "failure",
// latency: elapsedTime, // assuming elapsedTime holds the time taken before the error was caught
// sql: statement.sql
// });

// context.emit("event", { type: EventType.queryResults, data: { id, rows: [], fields: [], error: {
// message: error.toString(),
// position: error.position,
// query,
// }, elapsedTime }});
// break;
// } finally {
// resultsProvider._view?.show();
// }
// }
// } catch (err) {
// context.emit("event", { type: EventType.queryResults, data: { id, rows: [], fields: [], error: {
// message: "Syntax errors are present. For more information, please refer to the \"Problems\" tab.",
// position: undefined,
// query,
// }, elapsedTime: undefined }});

// console.error("[RunSQLCommand]", "Error running statement: ", err);
// }
// } catch (err) {
// context.emit("event", { type: EventType.queryResults, data: { id, rows: [], fields: [], error: {
// message: "Error connecting to Materialize.",
// position: undefined,
// query,
// }, elapsedTime: undefined }});
// }
// });

export const buildRunSQLCommand = (context: AsyncContext) => {
const sqlCommand = async () => {
const {
Expand Down
6 changes: 2 additions & 4 deletions src/providers/scripts/results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,11 @@
// Create data rows
// Loop through the data and create rows and cells
if (rows && rows.length > 0) {
rows.forEach((row: any, i: number) => {
rows.forEach((row: any) => {
const dataRow = document.createElement("vscode-data-grid-row");

fields.forEach(({ name: field }: { name: string }, index: number) => {
row.forEach((value: any, index: number) => {
const dataCell = document.createElement("vscode-data-grid-cell");
dataCell.setAttribute("grid-column", String(index + 1));
const value = row[field];
dataCell.innerText = typeof value === "object" ? JSON.stringify(value) : value;

dataRow.appendChild(dataCell);
Expand Down

0 comments on commit f2338d4

Please sign in to comment.