Merge pull request #9777 from Budibase/bug/budi-6619/cannot_update_relationships_in_postgresql_many_to_one

Bug - budi-6619 - Cannot update relationships in postgresql many to one
This commit is contained in:
Adria Navarro 2023-02-24 10:39:45 +00:00 committed by GitHub
commit ec1ed02c0d
5 changed files with 451 additions and 117 deletions

View File

@ -1,2 +1,2 @@
nodejs 14.19.3 nodejs 14.19.3
python 3.11.1 python 3.10.0

View File

@ -142,7 +142,11 @@ function cleanupConfig(config: RunConfig, table: Table): RunConfig {
return config return config
} }
function generateIdForRow(row: Row | undefined, table: Table): string { function generateIdForRow(
row: Row | undefined,
table: Table,
isLinked: boolean = false
): string {
const primary = table.primary const primary = table.primary
if (!row || !primary) { if (!row || !primary) {
return "" return ""
@ -150,8 +154,12 @@ function generateIdForRow(row: Row | undefined, table: Table): string {
// build id array // build id array
let idParts = [] let idParts = []
for (let field of primary) { for (let field of primary) {
// need to handle table name + field or just field, depending on if relationships used let fieldValue = extractFieldValue({
const fieldValue = row[`${table.name}.${field}`] || row[field] row,
tableName: table.name,
fieldName: field,
isLinked,
})
if (fieldValue) { if (fieldValue) {
idParts.push(fieldValue) idParts.push(fieldValue)
} }
@ -174,18 +182,52 @@ function getEndpoint(tableId: string | undefined, operation: string) {
} }
} }
function basicProcessing(row: Row, table: Table): Row { // need to handle table name + field or just field, depending on if relationships used
function extractFieldValue({
row,
tableName,
fieldName,
isLinked,
}: {
row: Row
tableName: string
fieldName: string
isLinked: boolean
}) {
let value = row[`${tableName}.${fieldName}`]
if (value == null && !isLinked) {
value = row[fieldName]
}
return value
}
function basicProcessing({
row,
table,
isLinked,
}: {
row: Row
table: Table
isLinked: boolean
}): Row {
const thisRow: Row = {} const thisRow: Row = {}
// filter the row down to what is actually the row (not joined) // filter the row down to what is actually the row (not joined)
for (let fieldName of Object.keys(table.schema)) { for (let field of Object.values(table.schema)) {
const pathValue = row[`${table.name}.${fieldName}`] const fieldName = field.name
const value = pathValue != null ? pathValue : row[fieldName]
const value = extractFieldValue({
row,
tableName: table.name,
fieldName,
isLinked,
})
// all responses include "select col as table.col" so that overlaps are handled // all responses include "select col as table.col" so that overlaps are handled
if (value != null) { if (value != null) {
thisRow[fieldName] = value thisRow[fieldName] = value
} }
} }
thisRow._id = generateIdForRow(row, table) thisRow._id = generateIdForRow(row, table, isLinked)
thisRow.tableId = table._id thisRow.tableId = table._id
thisRow._rev = "rev" thisRow._rev = "rev"
return processFormulas(table, thisRow) return processFormulas(table, thisRow)
@ -293,7 +335,7 @@ export class ExternalRequest {
// we're not inserting a doc, will be a bunch of update calls // we're not inserting a doc, will be a bunch of update calls
const otherKey: string = field.throughFrom || linkTablePrimary const otherKey: string = field.throughFrom || linkTablePrimary
const thisKey: string = field.throughTo || tablePrimary const thisKey: string = field.throughTo || tablePrimary
row[key].map((relationship: any) => { row[key].forEach((relationship: any) => {
manyRelationships.push({ manyRelationships.push({
tableId: field.through || field.tableId, tableId: field.through || field.tableId,
isUpdate: false, isUpdate: false,
@ -309,7 +351,7 @@ export class ExternalRequest {
const thisKey: string = "id" const thisKey: string = "id"
// @ts-ignore // @ts-ignore
const otherKey: string = field.fieldName const otherKey: string = field.fieldName
row[key].map((relationship: any) => { row[key].forEach((relationship: any) => {
manyRelationships.push({ manyRelationships.push({
tableId: field.tableId, tableId: field.tableId,
isUpdate: true, isUpdate: true,
@ -379,7 +421,8 @@ export class ExternalRequest {
) { ) {
continue continue
} }
let linked = basicProcessing(row, linkedTable)
let linked = basicProcessing({ row, table: linkedTable, isLinked: true })
if (!linked._id) { if (!linked._id) {
continue continue
} }
@ -427,7 +470,10 @@ export class ExternalRequest {
) )
continue continue
} }
const thisRow = fixArrayTypes(basicProcessing(row, table), table) const thisRow = fixArrayTypes(
basicProcessing({ row, table, isLinked: false }),
table
)
if (thisRow._id == null) { if (thisRow._id == null) {
throw "Unable to generate row ID for SQL rows" throw "Unable to generate row ID for SQL rows"
} }
@ -567,19 +613,41 @@ export class ExternalRequest {
const { key, tableId, isUpdate, id, ...rest } = relationship const { key, tableId, isUpdate, id, ...rest } = relationship
const body: { [key: string]: any } = processObjectSync(rest, row, {}) const body: { [key: string]: any } = processObjectSync(rest, row, {})
const linkTable = this.getTable(tableId) const linkTable = this.getTable(tableId)
// @ts-ignore const relationshipPrimary = linkTable?.primary || []
const linkPrimary = linkTable?.primary[0] const linkPrimary = relationshipPrimary[0]
if (!linkTable || !linkPrimary) { if (!linkTable || !linkPrimary) {
return return
} }
const linkSecondary = relationshipPrimary[1]
const rows = related[key]?.rows || [] const rows = related[key]?.rows || []
const found = rows.find(
(row: { [key: string]: any }) => function relationshipMatchPredicate({
row,
linkPrimary,
linkSecondary,
}: {
row: { [key: string]: any }
linkPrimary: string
linkSecondary?: string
}) {
const matchesPrimaryLink =
row[linkPrimary] === relationship.id || row[linkPrimary] === relationship.id ||
row[linkPrimary] === body?.[linkPrimary] row[linkPrimary] === body?.[linkPrimary]
if (!matchesPrimaryLink || !linkSecondary) {
return matchesPrimaryLink
}
const matchesSecondayLink = row[linkSecondary] === body?.[linkSecondary]
return matchesPrimaryLink && matchesSecondayLink
}
const existingRelationship = rows.find((row: { [key: string]: any }) =>
relationshipMatchPredicate({ row, linkPrimary, linkSecondary })
) )
const operation = isUpdate ? Operation.UPDATE : Operation.CREATE const operation = isUpdate ? Operation.UPDATE : Operation.CREATE
if (!found) { if (!existingRelationship) {
promises.push( promises.push(
getDatasourceAndQuery({ getDatasourceAndQuery({
endpoint: getEndpoint(tableId, operation), endpoint: getEndpoint(tableId, operation),
@ -590,7 +658,7 @@ export class ExternalRequest {
) )
} else { } else {
// remove the relationship from cache so it isn't adjusted again // remove the relationship from cache so it isn't adjusted again
rows.splice(rows.indexOf(found), 1) rows.splice(rows.indexOf(existingRelationship), 1)
} }
} }
// finally cleanup anything that needs to be removed // finally cleanup anything that needs to be removed
@ -629,10 +697,7 @@ export class ExternalRequest {
* Creating the specific list of fields that we desire, and excluding the ones that are no use to us * Creating the specific list of fields that we desire, and excluding the ones that are no use to us
* is more performant and has the added benefit of protecting against this scenario. * is more performant and has the added benefit of protecting against this scenario.
*/ */
buildFields( buildFields(table: Table, includeRelations: boolean) {
table: Table,
includeRelations: IncludeRelationship = IncludeRelationship.INCLUDE
) {
function extractRealFields(table: Table, existing: string[] = []) { function extractRealFields(table: Table, existing: string[] = []) {
return Object.entries(table.schema) return Object.entries(table.schema)
.filter( .filter(
@ -691,6 +756,10 @@ export class ExternalRequest {
} }
filters = buildFilters(id, filters || {}, table) filters = buildFilters(id, filters || {}, table)
const relationships = this.buildRelationships(table) const relationships = this.buildRelationships(table)
const includeSqlRelationships =
config.includeSqlRelationships === IncludeRelationship.INCLUDE
// clean up row on ingress using schema // clean up row on ingress using schema
const processed = this.inputProcessing(row, table) const processed = this.inputProcessing(row, table)
row = processed.row row = processed.row
@ -708,9 +777,7 @@ export class ExternalRequest {
}, },
resource: { resource: {
// have to specify the fields to avoid column overlap (for SQL) // have to specify the fields to avoid column overlap (for SQL)
fields: isSql fields: isSql ? this.buildFields(table, includeSqlRelationships) : [],
? this.buildFields(table, config.includeSqlRelationships)
: [],
}, },
filters, filters,
sort, sort,
@ -725,6 +792,7 @@ export class ExternalRequest {
table, table,
}, },
} }
// can't really use response right now // can't really use response right now
const response = await getDatasourceAndQuery(json) const response = await getDatasourceAndQuery(json)
// handle many to many relationships now if we know the ID (could be auto increment) // handle many to many relationships now if we know the ID (could be auto increment)

View File

@ -58,7 +58,7 @@ export async function patch(ctx: BBContext) {
return handleRequest(Operation.UPDATE, tableId, { return handleRequest(Operation.UPDATE, tableId, {
id: breakRowIdField(id), id: breakRowIdField(id),
row: inputs, row: inputs,
includeSqlRelationships: IncludeRelationship.EXCLUDE, includeSqlRelationships: IncludeRelationship.INCLUDE,
}) })
} }

View File

@ -27,7 +27,9 @@ describe("row api - postgres", () => {
let makeRequest: MakeRequestResponse, let makeRequest: MakeRequestResponse,
postgresDatasource: Datasource, postgresDatasource: Datasource,
primaryPostgresTable: Table, primaryPostgresTable: Table,
auxPostgresTable: Table oneToManyRelationshipInfo: ForeignTableInfo,
manyToOneRelationshipInfo: ForeignTableInfo,
manyToManyRelationshipInfo: ForeignTableInfo
let host: string let host: string
let port: number let port: number
@ -67,14 +69,17 @@ describe("row api - postgres", () => {
}, },
}) })
auxPostgresTable = await config.createTable({ async function createAuxTable(prefix: string) {
name: generator.word({ length: 10 }), return await config.createTable({
name: `${prefix}_${generator.word({ length: 6 })}`,
type: "external", type: "external",
primary: ["id"], primary: ["id"],
primaryDisplay: "title",
schema: { schema: {
id: { id: {
name: "id", name: "id",
type: FieldType.AUTO, type: FieldType.AUTO,
autocolumn: true,
constraints: { constraints: {
presence: true, presence: true,
}, },
@ -89,15 +94,33 @@ describe("row api - postgres", () => {
}, },
sourceId: postgresDatasource._id, sourceId: postgresDatasource._id,
}) })
}
oneToManyRelationshipInfo = {
table: await createAuxTable("o2m"),
fieldName: "oneToManyRelation",
relationshipType: RelationshipTypes.ONE_TO_MANY,
}
manyToOneRelationshipInfo = {
table: await createAuxTable("m2o"),
fieldName: "manyToOneRelation",
relationshipType: RelationshipTypes.MANY_TO_ONE,
}
manyToManyRelationshipInfo = {
table: await createAuxTable("m2m"),
fieldName: "manyToManyRelation",
relationshipType: RelationshipTypes.MANY_TO_MANY,
}
primaryPostgresTable = await config.createTable({ primaryPostgresTable = await config.createTable({
name: generator.word({ length: 10 }), name: `p_${generator.word({ length: 6 })}`,
type: "external", type: "external",
primary: ["id"], primary: ["id"],
schema: { schema: {
id: { id: {
name: "id", name: "id",
type: FieldType.AUTO, type: FieldType.AUTO,
autocolumn: true,
constraints: { constraints: {
presence: true, presence: true,
}, },
@ -117,25 +140,48 @@ describe("row api - postgres", () => {
name: "value", name: "value",
type: FieldType.NUMBER, type: FieldType.NUMBER,
}, },
linkedField: { oneToManyRelation: {
type: FieldType.LINK, type: FieldType.LINK,
constraints: { constraints: {
type: "array", type: "array",
presence: false, presence: false,
}, },
fieldName: "foreignField", fieldName: oneToManyRelationshipInfo.fieldName,
name: "linkedField", name: "oneToManyRelation",
relationshipType: RelationshipTypes.ONE_TO_MANY, relationshipType: RelationshipTypes.ONE_TO_MANY,
tableId: auxPostgresTable._id, tableId: oneToManyRelationshipInfo.table._id,
main: true,
},
manyToOneRelation: {
type: FieldType.LINK,
constraints: {
type: "array",
presence: false,
},
fieldName: manyToOneRelationshipInfo.fieldName,
name: "manyToOneRelation",
relationshipType: RelationshipTypes.MANY_TO_ONE,
tableId: manyToOneRelationshipInfo.table._id,
main: true,
},
manyToManyRelation: {
type: FieldType.LINK,
constraints: {
type: "array",
presence: false,
},
fieldName: manyToManyRelationshipInfo.fieldName,
name: "manyToManyRelation",
relationshipType: RelationshipTypes.MANY_TO_MANY,
tableId: manyToManyRelationshipInfo.table._id,
main: true,
}, },
}, },
sourceId: postgresDatasource._id, sourceId: postgresDatasource._id,
}) })
}) })
afterAll(async () => { afterAll(config.end)
await config.end()
})
function generateRandomPrimaryRowData() { function generateRandomPrimaryRowData() {
return { return {
@ -151,22 +197,99 @@ describe("row api - postgres", () => {
value: number value: number
} }
type ForeignTableInfo = {
table: Table
fieldName: string
relationshipType: RelationshipTypes
}
type ForeignRowsInfo = {
row: Row
relationshipType: RelationshipTypes
}
async function createPrimaryRow(opts: { async function createPrimaryRow(opts: {
rowData: PrimaryRowData rowData: PrimaryRowData
createForeignRow?: boolean createForeignRows?: {
createOneToMany?: boolean
createManyToOne?: number
createManyToMany?: number
}
}) { }) {
let { rowData } = opts let { rowData } = opts as any
let foreignRow: Row | undefined let foreignRows: ForeignRowsInfo[] = []
if (opts?.createForeignRow) {
foreignRow = await config.createRow({ async function createForeignRow(tableInfo: ForeignTableInfo) {
tableId: auxPostgresTable._id, const foreignKey = `fk_${tableInfo.table.name}_${tableInfo.fieldName}`
const foreignRow = await config.createRow({
tableId: tableInfo.table._id,
title: generator.name(), title: generator.name(),
}) })
rowData = { rowData = {
...rowData, ...rowData,
[`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id, [foreignKey]: foreignRow.id,
} }
foreignRows.push({
row: foreignRow,
relationshipType: tableInfo.relationshipType,
})
}
if (opts?.createForeignRows?.createOneToMany) {
const foreignKey = `fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`
const foreignRow = await config.createRow({
tableId: oneToManyRelationshipInfo.table._id,
title: generator.name(),
})
rowData = {
...rowData,
[foreignKey]: foreignRow.id,
}
foreignRows.push({
row: foreignRow,
relationshipType: oneToManyRelationshipInfo.relationshipType,
})
}
for (let i = 0; i < (opts?.createForeignRows?.createManyToOne || 0); i++) {
const foreignRow = await config.createRow({
tableId: manyToOneRelationshipInfo.table._id,
title: generator.name(),
})
rowData = {
...rowData,
[manyToOneRelationshipInfo.fieldName]:
rowData[manyToOneRelationshipInfo.fieldName] || [],
}
rowData[manyToOneRelationshipInfo.fieldName].push(foreignRow._id)
foreignRows.push({
row: foreignRow,
relationshipType: RelationshipTypes.MANY_TO_ONE,
})
}
for (let i = 0; i < (opts?.createForeignRows?.createManyToMany || 0); i++) {
const foreignRow = await config.createRow({
tableId: manyToManyRelationshipInfo.table._id,
title: generator.name(),
})
rowData = {
...rowData,
[manyToManyRelationshipInfo.fieldName]:
rowData[manyToManyRelationshipInfo.fieldName] || [],
}
rowData[manyToManyRelationshipInfo.fieldName].push(foreignRow._id)
foreignRows.push({
row: foreignRow,
relationshipType: RelationshipTypes.MANY_TO_MANY,
})
} }
const row = await config.createRow({ const row = await config.createRow({
@ -174,7 +297,7 @@ describe("row api - postgres", () => {
...rowData, ...rowData,
}) })
return { row, foreignRow } return { row, foreignRows }
} }
async function createDefaultPgTable() { async function createDefaultPgTable() {
@ -198,7 +321,9 @@ describe("row api - postgres", () => {
async function populatePrimaryRows( async function populatePrimaryRows(
count: number, count: number,
opts?: { opts?: {
createForeignRow?: boolean createOneToMany?: boolean
createManyToOne?: number
createManyToMany?: number
} }
) { ) {
return await Promise.all( return await Promise.all(
@ -210,7 +335,7 @@ describe("row api - postgres", () => {
rowData, rowData,
...(await createPrimaryRow({ ...(await createPrimaryRow({
rowData, rowData,
createForeignRow: opts?.createForeignRow, createForeignRows: opts,
})), })),
} }
}) })
@ -295,7 +420,7 @@ describe("row api - postgres", () => {
describe("given than a row exists", () => { describe("given than a row exists", () => {
let row: Row let row: Row
beforeEach(async () => { beforeEach(async () => {
let rowResponse = _.sample(await populatePrimaryRows(10))! let rowResponse = _.sample(await populatePrimaryRows(1))!
row = rowResponse.row row = rowResponse.row
}) })
@ -403,7 +528,7 @@ describe("row api - postgres", () => {
let rows: { row: Row; rowData: PrimaryRowData }[] let rows: { row: Row; rowData: PrimaryRowData }[]
beforeEach(async () => { beforeEach(async () => {
rows = await populatePrimaryRows(10) rows = await populatePrimaryRows(5)
}) })
it("a single row can be retrieved successfully", async () => { it("a single row can be retrieved successfully", async () => {
@ -419,34 +544,136 @@ describe("row api - postgres", () => {
describe("given a row with relation data", () => { describe("given a row with relation data", () => {
let row: Row let row: Row
let foreignRow: Row let rowData: {
name: string
description: string
value: number
}
let foreignRows: ForeignRowsInfo[]
describe("with all relationship types", () => {
beforeEach(async () => { beforeEach(async () => {
let [createdRow] = await populatePrimaryRows(1, { let [createdRow] = await populatePrimaryRows(1, {
createForeignRow: true, createOneToMany: true,
createManyToOne: 3,
createManyToMany: 2,
}) })
row = createdRow.row row = createdRow.row
foreignRow = createdRow.foreignRow! rowData = createdRow.rowData
foreignRows = createdRow.foreignRows
}) })
it("only foreign keys are retrieved", async () => { it("only one to many foreign keys are retrieved", async () => {
const res = await getRow(primaryPostgresTable._id, row.id) const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
const one2ManyForeignRows = foreignRows.filter(
x => x.relationshipType === RelationshipTypes.ONE_TO_MANY
)
expect(one2ManyForeignRows).toHaveLength(1)
expect(res.body).toEqual({ expect(res.body).toEqual({
...row, ...rowData,
id: row.id,
tableId: row.tableId,
_id: expect.any(String),
_rev: expect.any(String),
[`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]:
one2ManyForeignRows[0].row.id,
})
expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined()
})
})
describe("with only one to many", () => {
beforeEach(async () => {
let [createdRow] = await populatePrimaryRows(1, {
createOneToMany: true,
})
row = createdRow.row
rowData = createdRow.rowData
foreignRows = createdRow.foreignRows
})
it("only one to many foreign keys are retrieved", async () => {
const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200)
expect(foreignRows).toHaveLength(1)
expect(res.body).toEqual({
...rowData,
id: row.id,
tableId: row.tableId,
_id: expect.any(String),
_rev: expect.any(String),
[`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]:
foreignRows[0].row.id,
})
expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined()
})
})
describe("with only many to one", () => {
beforeEach(async () => {
let [createdRow] = await populatePrimaryRows(1, {
createManyToOne: 3,
})
row = createdRow.row
rowData = createdRow.rowData
foreignRows = createdRow.foreignRows
})
it("only one to many foreign keys are retrieved", async () => {
const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200)
expect(foreignRows).toHaveLength(3)
expect(res.body).toEqual({
...rowData,
id: row.id,
tableId: row.tableId,
_id: expect.any(String), _id: expect.any(String),
_rev: expect.any(String), _rev: expect.any(String),
}) })
expect(res.body.foreignField).toBeUndefined() expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined()
})
})
expect( describe("with only many to many", () => {
res.body[`fk_${auxPostgresTable.name}_foreignField`] beforeEach(async () => {
).toBeDefined() let [createdRow] = await populatePrimaryRows(1, {
expect(res.body[`fk_${auxPostgresTable.name}_foreignField`]).toBe( createManyToMany: 2,
foreignRow.id })
) row = createdRow.row
rowData = createdRow.rowData
foreignRows = createdRow.foreignRows
})
it("only one to many foreign keys are retrieved", async () => {
const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200)
expect(foreignRows).toHaveLength(2)
expect(res.body).toEqual({
...rowData,
id: row.id,
tableId: row.tableId,
_id: expect.any(String),
_rev: expect.any(String),
})
expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined()
})
}) })
}) })
}) })
@ -667,31 +894,74 @@ describe("row api - postgres", () => {
const getAll = (tableId: string | undefined, rowId: string | undefined) => const getAll = (tableId: string | undefined, rowId: string | undefined) =>
makeRequest("get", `/api/${tableId}/${rowId}/enrich`) makeRequest("get", `/api/${tableId}/${rowId}/enrich`)
describe("given a row with relation data", () => { describe("given a row with relation data", () => {
let row: Row, foreignRow: Row | undefined let row: Row, rowData: PrimaryRowData, foreignRows: ForeignRowsInfo[]
describe("with all relationship types", () => {
beforeEach(async () => { beforeEach(async () => {
rowData = generateRandomPrimaryRowData()
const rowsInfo = await createPrimaryRow({ const rowsInfo = await createPrimaryRow({
rowData: generateRandomPrimaryRowData(), rowData,
createForeignRow: true, createForeignRows: {
createOneToMany: true,
createManyToOne: 3,
createManyToMany: 2,
},
}) })
row = rowsInfo.row row = rowsInfo.row
foreignRow = rowsInfo.foreignRow foreignRows = rowsInfo.foreignRows
}) })
it("enrich populates the foreign field", async () => { it("enrich populates the foreign fields", async () => {
const res = await getAll(primaryPostgresTable._id, row.id) const res = await getAll(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(foreignRow).toBeDefined() const foreignRowsByType = _.groupBy(
foreignRows,
x => x.relationshipType
)
expect(res.body).toEqual({ expect(res.body).toEqual({
...row, ...rowData,
linkedField: [ [`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]:
foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row.id,
[oneToManyRelationshipInfo.fieldName]: [
{ {
...foreignRow, ...foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row,
_id: expect.any(String),
_rev: expect.any(String),
}, },
], ],
[manyToOneRelationshipInfo.fieldName]: [
{
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][0].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id,
},
{
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][1].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id,
},
{
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][2].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id,
},
],
[manyToManyRelationshipInfo.fieldName]: [
{
...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][0].row,
},
{
...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][1].row,
},
],
id: row.id,
tableId: row.tableId,
_id: expect.any(String),
_rev: expect.any(String),
})
}) })
}) })
}) })
@ -714,7 +984,7 @@ describe("row api - postgres", () => {
const rowsCount = 6 const rowsCount = 6
let rows: { let rows: {
row: Row row: Row
foreignRow: Row | undefined foreignRows: ForeignRowsInfo[]
rowData: PrimaryRowData rowData: PrimaryRowData
}[] }[]
beforeEach(async () => { beforeEach(async () => {

View File

@ -415,9 +415,7 @@ class InternalBuilder {
if (opts.disableReturning) { if (opts.disableReturning) {
return query.insert(parsedBody) return query.insert(parsedBody)
} else { } else {
return query return query.insert(parsedBody).returning("*")
.insert(parsedBody)
.returning(generateSelectStatement(json, knex))
} }
} }
@ -502,9 +500,7 @@ class InternalBuilder {
if (opts.disableReturning) { if (opts.disableReturning) {
return query.update(parsedBody) return query.update(parsedBody)
} else { } else {
return query return query.update(parsedBody).returning("*")
.update(parsedBody)
.returning(generateSelectStatement(json, knex))
} }
} }