Browse Source

fix: escape _ in id value if composite pk

pull/9683/head
Pranav C 2 months ago
parent
commit
1107111e6d
  1. 6
      packages/nc-gui/composables/useData.ts
  2. 5
      packages/nc-gui/composables/useKanbanViewStore.ts
  3. 15
      packages/nc-gui/composables/useLTARStore.ts
  4. 12
      packages/nc-gui/utils/dataUtils.ts
  5. 17
      packages/nocodb/src/db/BaseModelSqlv2.ts
  6. 33
      packages/nocodb/src/helpers/catchError.ts
  7. 6
      packages/nocodb/src/services/data-table.service.ts

6
packages/nc-gui/composables/useData.ts

@ -1,6 +1,7 @@
import type { ColumnType, LinkToAnotherRecordType, PaginatedType, RelationTypes, TableType, ViewType } from 'nocodb-sdk'
import { UITypes, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue'
import { extractPkFromRow } from '../utils/dataUtils'
import type { CellRange } from '#imports'
export function useData(args: {
@ -550,10 +551,7 @@ export function useData(args: {
try {
const row = formattedData.value[rowIndex]
if (!row.rowMeta.new) {
const id = meta?.value?.columns
?.filter((c) => c.pk)
.map((c) => row.row[c.title!])
.join('___')
const id = extractPkFromRow(row.row, meta?.value?.columns)
const fullRecord = await $api.dbTableRow.read(
NOCO,

5
packages/nc-gui/composables/useKanbanViewStore.ts

@ -659,10 +659,7 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
}
if (!row.rowMeta.new) {
const id = (meta?.value?.columns as ColumnType[])
?.filter((c) => c.pk)
.map((c) => row.row[c.title!])
.join('___')
const id = extractPkFromRow(row.row, meta?.value?.columns)
const deleted = await deleteRowById(id as string)
if (!deleted) {

15
packages/nc-gui/composables/useLTARStore.ts

@ -7,6 +7,7 @@ import type {
} from 'nocodb-sdk'
import { RelationTypes, UITypes, dateFormats, parseStringDateTime, timeFormats } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue'
import { extractPkFromRow } from '../utils/dataUtils'
interface DataApiResponse {
list: Record<string, any>
@ -88,18 +89,12 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
return metas.value?.[colOptions.value?.fk_related_model_id as string]
})
const rowId = computed(() =>
meta.value.columns
.filter((c: Required<ColumnType>) => c.pk)
.map((c: Required<ColumnType>) => row?.value?.row?.[c.title])
.join('___'),
)
const rowId = computed(() => {
extractPkFromRow(row.value.row, meta.value)
})
const getRelatedTableRowId = (row: Record<string, any>) => {
return relatedTableMeta.value?.columns
?.filter((c) => c.pk)
.map((c) => row?.[c.title as string] ?? row?.[c.id as string])
.join('___')
return extractPkFromRow(row, relatedTableMeta.value?.columns)
}
// actions

12
packages/nc-gui/utils/dataUtils.ts

@ -26,11 +26,13 @@ export const isValidValue = (val: unknown) => {
export const extractPkFromRow = (row: Record<string, any>, columns: ColumnType[]) => {
if (!row || !columns) return null
const pkColumns = columns.filter((c) => c.pk)
if (pkColumns.every((c) => row?.[c.title as string] === null || row?.[c.title as string] === undefined)) return null
return pkColumns.map((c) => row?.[c.title as string]).join('___')
const pkCols = columns.filter((c: Required<ColumnType>) => c.pk)
// if multiple pk columns, join them with ___ and escape _ in id values with \_ to avoid conflicts
if (pkCols.length > 1) {
pkCols.map((c: Required<ColumnType>) => row?.[c.title]?.toString?.().replaceAll('_', '\\_') ?? null).join('___')
} else if (pkCols.length) {
return row?.[pkCols[0].title] ?? null
}
}
export const rowPkData = (row: Record<string, any>, columns: ColumnType[]) => {

17
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -6517,7 +6517,11 @@ class BaseModelSqlv2 {
for (const pk of this.model.primaryKeys) {
pkValues[pk.title] = data[pk.title] ?? data[pk.column_name];
}
return asString ? Object.values(pkValues).join('___') : pkValues;
return asString
? Object.values(pkValues)
.map((val) => val?.toString?.().replaceAll('_', '\\_'))
.join('___')
: pkValues;
} else if (this.model.primaryKey) {
return (
data[this.model.primaryKey.title] ??
@ -10225,7 +10229,16 @@ export function getCompositePkValue(primaryKeys: Column[], row) {
if (typeof row !== 'object') return row;
return primaryKeys.map((c) => row[c.title] ?? row[c.column_name]).join('___');
if (primaryKeys.length > 1) {
primaryKeys.map((c) =>
(row[c.title] ?? row[c.column_name])?.toString?.().replaceAll('_', '\\_'),
);
}
return (
primaryKeys[0] &&
(row[primaryKeys[0].title] ?? row[primaryKeys[0].column_name])
);
}
export function haveFormulaColumn(columns: Column[]) {

33
packages/nocodb/src/helpers/catchError.ts

@ -795,22 +795,43 @@ export class NcError {
id: string | string[] | Record<string, string> | Record<string, string>[],
args?: NcErrorArgs,
) {
let formatedId: string | string[] = '';
if (!id) {
id = 'unknown';
formatedId = 'unknown';
} else if (typeof id === 'string') {
id = [id];
formatedId = [id];
} else if (Array.isArray(id)) {
if (id.every((i) => typeof i === 'string')) {
id = id as string[];
formatedId = id as string[];
} else {
id = id.map((i) => Object.values(i).join('___'));
formatedId = id.map((val) => {
const idsArr = Object.values(val);
if (idsArr.length > 1) {
return idsArr
.map((idVal) => idVal?.toString?.().replaceAll('_', '\\_'))
.join('___');
} else if (idsArr.length) {
return idsArr[0];
} else {
return 'unknown';
}
});
}
} else {
id = Object.values(id).join('___');
const idsArr = Object.values(id);
if (idsArr.length > 1) {
idsArr
.map((idVal) => idVal?.toString?.().replaceAll('_', '\\_'))
.join('___');
} else if (idsArr.length) {
formatedId = idsArr[0];
} else {
formatedId = 'unknown';
}
}
throw new NcBaseErrorv2(NcErrorType.RECORD_NOT_FOUND, {
params: id,
params: formatedId,
...args,
});
}

6
packages/nocodb/src/services/data-table.service.ts

@ -315,7 +315,11 @@ export class DataTableService {
// if composite primary key then join the values with ___
else
pk = model.primaryKeys
.map((pk) => row[pk.title] ?? row[pk.column_name])
.map((pk) =>
(row[pk.title] ?? row[pk.column_name])
?.toString?.()
?.replaceAll('_', '\\_'),
)
.join('___');
// if duplicate then throw error
if (keys.has(pk)) {

Loading…
Cancel
Save