diff --git a/lerna.json b/lerna.json index 8d7179dbae..c02b221ec8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.2.44", + "version": "3.2.45", "npmClient": "yarn", "concurrency": 20, "command": { diff --git a/packages/backend-core/src/db/couch/connections.ts b/packages/backend-core/src/db/couch/connections.ts index 5c7d7ec81d..9692b095a8 100644 --- a/packages/backend-core/src/db/couch/connections.ts +++ b/packages/backend-core/src/db/couch/connections.ts @@ -1,6 +1,6 @@ import env from "../../environment" -export const getCouchInfo = (connection?: string) => { +export const getCouchInfo = (connection?: string | null) => { // clean out any auth credentials const urlInfo = getUrlInfo(connection) let username @@ -45,7 +45,7 @@ export const getCouchInfo = (connection?: string) => { } } -export const getUrlInfo = (url = env.COUCH_DB_URL) => { +export const getUrlInfo = (url: string | null = env.COUCH_DB_URL) => { let cleanUrl, username, password, host if (url) { // Ensure the URL starts with a protocol diff --git a/packages/backend-core/src/db/tests/pouch.spec.js b/packages/backend-core/src/db/tests/pouch.spec.ts similarity index 98% rename from packages/backend-core/src/db/tests/pouch.spec.js rename to packages/backend-core/src/db/tests/pouch.spec.ts index f0abc82240..21632cff88 100644 --- a/packages/backend-core/src/db/tests/pouch.spec.js +++ b/packages/backend-core/src/db/tests/pouch.spec.ts @@ -1,5 +1,6 @@ require("../../../tests") -const getUrlInfo = require("../couch").getUrlInfo + +import { getUrlInfo } from "../couch" describe("pouch", () => { describe("Couch DB URL parsing", () => { diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index 5a6907faa0..334f1efdd4 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -1172,20 +1172,22 @@ class InternalBuilder { nulls = value.direction === SortOrder.ASCENDING ? "first" : "last" } + const composite = `${aliased}.${key}` + let identifier + if (this.isAggregateField(key)) { - query = query.orderBy(key, direction, nulls) + identifier = this.rawQuotedIdentifier(key) + } else if (this.client === SqlClient.ORACLE) { + identifier = this.convertClobs(composite) } else { - let composite = `${aliased}.${key}` - if (this.client === SqlClient.ORACLE) { - query = query.orderByRaw(`?? ?? nulls ??`, [ - this.convertClobs(composite), - this.knex.raw(direction), - this.knex.raw(nulls as string), - ]) - } else { - query = query.orderBy(composite, direction, nulls) - } + identifier = this.rawQuotedIdentifier(composite) } + + query = query.orderByRaw(`?? ?? ${nulls ? "nulls ??" : ""}`, [ + identifier, + this.knex.raw(direction), + ...(nulls ? [this.knex.raw(nulls as string)] : []), + ]) } } @@ -1344,14 +1346,16 @@ class InternalBuilder { // add the correlation to the overall query subQuery = subQuery.where( - correlatedTo, + this.rawQuotedIdentifier(correlatedTo), "=", this.rawQuotedIdentifier(correlatedFrom) ) const standardWrap = (select: Knex.Raw): Knex.QueryBuilder => { subQuery = subQuery - .select(relationshipFields) + .select( + relationshipFields.map(field => this.rawQuotedIdentifier(field)) + ) .limit(getRelationshipLimit()) // @ts-ignore - the from alias syntax isn't in Knex typing return knex.select(select).from({ diff --git a/packages/backend-core/tests/core/users/users.spec.js b/packages/backend-core/tests/core/users/users.spec.ts similarity index 67% rename from packages/backend-core/tests/core/users/users.spec.js rename to packages/backend-core/tests/core/users/users.spec.ts index dde0d87fb7..b14f553266 100644 --- a/packages/backend-core/tests/core/users/users.spec.js +++ b/packages/backend-core/tests/core/users/users.spec.ts @@ -1,17 +1,17 @@ -const _ = require("lodash/fp") -const { structures } = require("../../../tests") +import { range } from "lodash/fp" +import { structures } from "../.." jest.mock("../../../src/context") jest.mock("../../../src/db") -const context = require("../../../src/context") -const db = require("../../../src/db") +import * as context from "../../../src/context" +import * as db from "../../../src/db" -const { getCreatorCount } = require("../../../src/users/users") +import { getCreatorCount } from "../../../src/users/users" describe("Users", () => { - let getGlobalDBMock - let paginationMock + let getGlobalDBMock: jest.SpyInstance + let paginationMock: jest.SpyInstance beforeEach(() => { jest.resetAllMocks() @@ -22,11 +22,10 @@ describe("Users", () => { jest.spyOn(db, "getGlobalUserParams") }) - it("Retrieves the number of creators", async () => { - const getUsers = (offset, limit, creators = false) => { - const range = _.range(offset, limit) + it("retrieves the number of creators", async () => { + const getUsers = (offset: number, limit: number, creators = false) => { const opts = creators ? { builder: { global: true } } : undefined - return range.map(() => structures.users.user(opts)) + return range(offset, limit).map(() => structures.users.user(opts)) } const page1Data = getUsers(0, 8) const page2Data = getUsers(8, 12, true) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index db5fcbaebb..576f0bb663 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -3650,6 +3650,51 @@ if (descriptions.length) { }) }) + if (isInternal || isMSSQL) { + describe("Fields with spaces", () => { + let table: Table + let otherTable: Table + let relatedRow: Row + + beforeAll(async () => { + otherTable = await config.api.table.save(defaultTable()) + table = await config.api.table.save( + saveTableRequest({ + schema: { + links: { + name: "links", + fieldName: "links", + type: FieldType.LINK, + tableId: otherTable._id!, + relationshipType: RelationshipType.ONE_TO_MANY, + }, + "nameWithSpace ": { + name: "nameWithSpace ", + type: FieldType.STRING, + }, + }, + }) + ) + relatedRow = await config.api.row.save(otherTable._id!, { + name: generator.word(), + description: generator.paragraph(), + }) + await config.api.row.save(table._id!, { + "nameWithSpace ": generator.word(), + tableId: table._id!, + links: [relatedRow._id], + }) + }) + + it("Successfully returns rows that have spaces in their field names", async () => { + const { rows } = await config.api.row.search(table._id!) + expect(rows.length).toBe(1) + const row = rows[0] + expect(row["nameWithSpace "]).toBeDefined() + }) + }) + } + if (!isInternal && !isOracle) { describe("bigint ids", () => { let table1: Table, table2: Table diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index 477813239b..8548d57f15 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -276,6 +276,7 @@ class SqlServerIntegration extends Sql implements DatasourcePlus { encrypt, enableArithAbort: true, requestTimeout: env.QUERY_THREAD_TIMEOUT, + connectTimeout: env.QUERY_THREAD_TIMEOUT, }, } if (encrypt) {