diff --git a/src/Explorer/Tables/CqlUtilities.test.ts b/src/Explorer/Tables/CqlUtilities.test.ts new file mode 100644 index 000000000..0049bf1fa --- /dev/null +++ b/src/Explorer/Tables/CqlUtilities.test.ts @@ -0,0 +1,18 @@ +import { getQuotedCqlIdentifier } from "./CqlUtilities"; + +describe("getQuotedCqlIdentifier", () => { + it("undefined id", () => { + const result = getQuotedCqlIdentifier(undefined); + expect(result).toBe(undefined); + }); + + it("id with no quotes", () => { + const result = getQuotedCqlIdentifier("foo"); + expect(result).toBe('"foo"'); + }); + + it("id with quotes", () => { + const result = getQuotedCqlIdentifier('"foo"'); + expect(result).toBe('"""foo"""'); + }); +}); diff --git a/src/Explorer/Tables/CqlUtilities.ts b/src/Explorer/Tables/CqlUtilities.ts new file mode 100644 index 000000000..52decfef0 --- /dev/null +++ b/src/Explorer/Tables/CqlUtilities.ts @@ -0,0 +1,12 @@ +export function getQuotedCqlIdentifier(identifier: string): string { + let result = identifier; + if (!identifier) { + return result; + } + + if (identifier.includes('"')) { + result = identifier.replace(/"/g, '""'); + } + + return `"${result}"`; +} diff --git a/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts b/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts index 23a2ae132..0ae172404 100644 --- a/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts +++ b/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts @@ -6,6 +6,7 @@ import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import { CassandraTableKey, CassandraAPIDataClient } from "../TableDataClient"; import DataTableViewModel from "./DataTableViewModel"; import * as DataTableUtilities from "./DataTableUtilities"; +import { getQuotedCqlIdentifier } from "../CqlUtilities"; import TableCommands from "./TableCommands"; import TableEntityCache from "./TableEntityCache"; import * as Constants from "../Constants"; @@ -57,7 +58,9 @@ export default class TableEntityListViewModel extends DataTableViewModel { this.queryTablesTab = queryTablesTab; this.id = `tableEntityListViewModel${this.queryTablesTab.tabId}`; this.cqlQuery = ko.observable( - `SELECT * FROM ${this.queryTablesTab.collection.databaseId}.${this.queryTablesTab.collection.id()}` + `SELECT * FROM ${getQuotedCqlIdentifier(this.queryTablesTab.collection.databaseId)}.${getQuotedCqlIdentifier( + this.queryTablesTab.collection.id() + )}` ); this.oDataQuery = ko.observable(); this.sqlQuery = ko.observable("SELECT * FROM c"); diff --git a/src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts b/src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts index feb661203..3d0c26092 100644 --- a/src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts +++ b/src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts @@ -1,5 +1,6 @@ import * as ko from "knockout"; import * as CustomTimestampHelper from "./CustomTimestampHelper"; +import { getQuotedCqlIdentifier } from "../CqlUtilities"; import QueryClauseViewModel from "./QueryClauseViewModel"; import ClauseGroup from "./ClauseGroup"; import ClauseGroupViewModel from "./ClauseGroupViewModel"; @@ -237,7 +238,7 @@ export default class QueryBuilderViewModel { public getCqlFilterFromClauses = (): string => { const databaseId = this._queryViewModel.queryTablesTab.collection.databaseId; const collectionId = this._queryViewModel.queryTablesTab.collection.id(); - const tableToQuery = `${databaseId}.${collectionId}`; + const tableToQuery = `${getQuotedCqlIdentifier(databaseId)}.${getQuotedCqlIdentifier(collectionId)}`; var filterString: string = `SELECT * FROM ${tableToQuery}`; if (this._queryViewModel.selectText() && this._queryViewModel.selectText().length > 0) { filterString = "SELECT"; diff --git a/src/Explorer/Tables/QueryBuilder/QueryViewModel.ts b/src/Explorer/Tables/QueryBuilder/QueryViewModel.ts index 3ec8e42b2..058c46f93 100644 --- a/src/Explorer/Tables/QueryBuilder/QueryViewModel.ts +++ b/src/Explorer/Tables/QueryBuilder/QueryViewModel.ts @@ -7,6 +7,7 @@ import TableEntityListViewModel from "../DataTable/TableEntityListViewModel"; import QueryTablesTab from "../../Tabs/QueryTablesTab"; import * as DataTableUtilities from "../DataTable/DataTableUtilities"; import { KeyCodes } from "../../../Common/Constants"; +import { getQuotedCqlIdentifier } from "../CqlUtilities"; export default class QueryViewModel { public topValueLimitMessage: string = "Please input a number between 0 and 1000."; @@ -189,7 +190,9 @@ export default class QueryViewModel { this._tableEntityListViewModel.oDataQuery(""); this._tableEntityListViewModel.sqlQuery("SELECT * FROM c"); this._tableEntityListViewModel.cqlQuery( - `SELECT * FROM ${this.queryTablesTab.collection.databaseId}.${this.queryTablesTab.collection.id()}` + `SELECT * FROM ${getQuotedCqlIdentifier(this.queryTablesTab.collection.databaseId)}.${getQuotedCqlIdentifier( + this.queryTablesTab.collection.id() + )}` ); return this._tableEntityListViewModel.reloadTable(false); };