Browse Source

feat: relation across bases

pull/8367/head
Pranav C 4 months ago
parent
commit
0ee27e716c
  1. 2
      packages/nc-gui/components/smartsheet/Cell.vue
  2. 17
      packages/nc-gui/components/smartsheet/PlainCell.vue
  3. 86
      packages/nc-gui/components/smartsheet/column/LinkAdvancedOptions.vue
  4. 2
      packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue
  5. 175
      packages/nocodb/src/db/BaseModelSqlv2.ts
  6. 33
      packages/nocodb/src/db/genRollupSelectv2.ts
  7. 12
      packages/nocodb/src/services/columns.service.ts
  8. 1
      packages/nocodb/tsconfig.json

2
packages/nc-gui/components/smartsheet/Cell.vue

@ -51,7 +51,7 @@ const { currentRow } = useSmartsheetRowStoreOrThrow()
const { sqlUis } = storeToRefs(useBase()) const { sqlUis } = storeToRefs(useBase())
const sqlUi = ref(column.value?.source_id ? sqlUis.value[column.value?.source_id] : Object.values(sqlUis.value)[0]) const sqlUi = ref(column.value?.source_id && sqlUis.value[column.value?.source_id] ? sqlUis.value[column.value?.source_id] : Object.values(sqlUis.value)[0])
const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value)) const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value))

17
packages/nc-gui/components/smartsheet/PlainCell.vue

@ -37,7 +37,9 @@ const { basesUser } = storeToRefs(basesStore)
const { isXcdbBase, isMssql, isMysql } = useBase() const { isXcdbBase, isMssql, isMysql } = useBase()
const sqlUi = ref(column.value?.source_id ? sqlUis.value[column.value?.source_id] : Object.values(sqlUis.value)[0]) const { getPossibleAttachmentSrc } = useAttachment()
const sqlUi = ref(column.value?.source_id && sqlUis.value[column.value?.source_id] ? sqlUis.value[column.value?.source_id] : Object.values(sqlUis.value)[0])
const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value)) const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value))
@ -246,7 +248,7 @@ const getLookupValue = (modelValue: string | null | number | Array<any>, col: Co
const getAttachmentValue = (modelValue: string | null | number | Array<any>) => { const getAttachmentValue = (modelValue: string | null | number | Array<any>) => {
if (Array.isArray(modelValue)) { if (Array.isArray(modelValue)) {
return modelValue.map((v) => `${v.title}`).join(', ') return modelValue.map((v) => `${v.title} (${getPossibleAttachmentSrc(v).join(', ')})`).join(', ')
} }
return modelValue as string return modelValue as string
} }
@ -341,20 +343,20 @@ const parseValue = (value: any, col: ColumnType): string => {
<template> <template>
<span <span
class="plain-cell before:px-1" class="calendar-cell text-xs before:px-1"
:class="{ :class="{
'!font-bold': bold, 'font-bold': bold,
'italic': italic, 'italic': italic,
'underline': underline, 'underline': underline,
}" }"
data-testid="nc-plain-cell" data-testid="nc-calendar-cell"
> >
{{ parseValue(modelValue, column) }} {{ parseValue(modelValue, column) }}
</span> </span>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.plain-cell { .calendar-cell {
&::before { &::before {
content: '•'; content: '•';
padding: 0 4px; padding: 0 4px;
@ -363,8 +365,5 @@ const parseValue = (value: any, col: ColumnType): string => {
content: ''; content: '';
padding: 0; padding: 0;
} }
&:first-child {
padding-left: 0;
}
} }
</style> </style>

86
packages/nc-gui/components/smartsheet/column/LinkAdvancedOptions.vue

@ -1,14 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumnCreateStoreOrThrow, useVModel, computed, inject, MetaInj, ref, useI18n,useBase, storeToRefs, useMetas } from '#imports' import { useColumnCreateStoreOrThrow, useVModel, computed, inject, MetaInj, ref, useI18n,useBase,
useBases, storeToRefs, useMetas } from '#imports'
import { import {
isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedByCol,
isCreatedOrLastModifiedTimeCol, isCreatedOrLastModifiedTimeCol,
isVirtualCol, isVirtualCol,
ModelTypes, ModelTypes,
RelationTypes, RelationTypes,
UITypes
} from "nocodb-sdk"; } from "nocodb-sdk";
import type {ColumnType} from "nocodb-sdk"; import {useTable} from "../../../composables/useTable";
import {useTablesStore} from "../../../store/tables";
const props = defineProps<{ const props = defineProps<{
value: any value: any
@ -25,7 +26,7 @@ const vModel = useVModel(props, 'value', emit)
const { validateInfos, setAdditionalValidations, onDataTypeChange } = useColumnCreateStoreOrThrow() const { validateInfos, setAdditionalValidations, onDataTypeChange } = useColumnCreateStoreOrThrow()
const baseStore = useBase() const baseStore = useBase()
const { tables } = storeToRefs(baseStore) // const { tables } = storeToRefs(baseStore)
const { metas, getMeta } = useMetas() const { metas, getMeta } = useMetas()
@ -35,16 +36,30 @@ const isMm = computed(() => vModel.value.type === RelationTypes.MANY_TO_MANY)
// set default value // set default value
vModel.value.custom = { vModel.value.custom = {
base_id: meta.value?.base_id
} }
const refTables = computed(() =>{
const { basesList, bases } = storeToRefs(useBases())
const tablesStore = useTablesStore()
const { baseTables } = storeToRefs(tablesStore)
/*const refTables = computed(() =>{
if (!tables.value || !tables.value.length) { if (!tables.value || !tables.value.length) {
return [] return []
} }
return tables.value.filter((t) => t.type === ModelTypes.TABLE && t.source_id === meta.value?.source_id) return tables.value.filter((t) => t.type === ModelTypes.TABLE && t.source_id === meta.value?.source_id)
})*/
const refTables = computed(() =>{
if (!baseTables.value.get(vModel.value.custom.base_id)) {
return []
}
return [...baseTables.value.get(vModel.value.custom.base_id).filter((t) => t.type === ModelTypes.TABLE),
...(vModel.value.custom.base_id !== meta.value?.base_id ? baseTables.value.get(meta.value.base_id).filter((t) => t.type === ModelTypes.TABLE) : [])
]
}) })
@ -75,30 +90,37 @@ const juncTableColumns = computed(() => {
}) })
const filterOption = (value: string, option: { key: string }) => option.key.toLowerCase().includes(value.toLowerCase()) const filterOption = (value: string, option: { key: string }) => option.key.toLowerCase().includes(value.toLowerCase())
const onModelIdChange =async (modelId:string) =>{ const onModelIdChange =async (modelId:string) =>{
// todo: optimise
await getMeta(modelId, false, false, vModel.value.custom.base_id)
await getMeta(modelId) await getMeta(modelId)
await onDataTypeChange() await onDataTypeChange()
} }
const onBaseChange = async (baseId) =>{
await tablesStore.loadProjectTables(baseId)
vModel.value.custom.ref_model_id = null
}
</script> </script>
<template> <template>
<div v-if="validateInfos"> <div v-if="validateInfos">
<div class="flex flex-row space-x-2"> <div class="flex flex-row space-x-2">
<a-form-item <a-form-item
class="flex w-full pb-2 mt-4 nc-ltar-child-table" class="flex w-full pb-2 mt-4 nc-ltar-child-table"
label="Column" label="Column"
v-bind="validateInfos['custom.column_id']" v-bind="validateInfos['custom.column_id']"
> >
<a-select <a-select
v-model:value="vModel.custom.column_id" v-model:value="vModel.custom.column_id"
show-search show-search
:filter-option="filterOption" :filter-option="filterOption"
dropdown-class-name="nc-dropdown-ltar-child-table" dropdown-class-name="nc-dropdown-ltar-child-table"
@change="onDataTypeChange" @change="onDataTypeChange"
> >
<a-select-option v-for="column of columns" :key="column.title" :value="column.id"> <a-select-option v-for="column of columns" :key="column.title" :value="column.id">
<div class="flex w-full items-center gap-2"> <div class="flex w-full items-center gap-2">
@ -113,6 +135,32 @@ const onModelIdChange =async (modelId:string) =>{
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item
class="flex w-full pb-2 mt-4 nc-ltar-child-table"
label="Target base"
v-bind="validateInfos['custom.base_id']"
>
<a-select
v-model:value="vModel.custom.base_id"
show-search
:filter-option="filterOption"
dropdown-class-name="nc-dropdown-ltar-child-table"
@change="onBaseChange(vModel.custom.base_id)"
>
<a-select-option v-for="base of basesList" :key="base.title" :value="base.id">
<div class="flex w-full items-center gap-2">
<div class="min-w-5 flex items-center justify-center">
<GeneralTableIcon :meta="base" class="text-gray-500" />
</div>
<NcTooltip class="flex-1 truncate" show-on-truncate-only>
<template #title>{{ base.title }}</template>
<span>{{ base.title }}</span>
</NcTooltip>
</div>
</a-select-option>
</a-select>
</a-form-item>
</div> </div>
<div class="flex flex-row space-x-2"> <div class="flex flex-row space-x-2">
<a-form-item <a-form-item
@ -127,14 +175,14 @@ const onModelIdChange =async (modelId:string) =>{
dropdown-class-name="nc-dropdown-ltar-child-table" dropdown-class-name="nc-dropdown-ltar-child-table"
@change="onModelIdChange(vModel.custom.ref_model_id)" @change="onModelIdChange(vModel.custom.ref_model_id)"
> >
<a-select-option v-for="table of tables" :key="table.title" :value="table.id"> <a-select-option v-for="table of refTables" :key="table.title" :value="table.id">
<div class="flex w-full items-center gap-2"> <div class="flex w-full items-center gap-2">
<div class="min-w-5 flex items-center justify-center"> <div class="min-w-5 flex items-center justify-center">
<GeneralTableIcon :meta="table" class="text-gray-500" /> <GeneralTableIcon :meta="table" class="text-gray-500" />
</div> </div>
<NcTooltip class="flex-1 truncate" show-on-truncate-only> <NcTooltip class="flex-1 truncate" show-on-truncate-only>
<template #title>{{ table.title }}</template> <template #title>{{ table.title }}</template>
<span>{{ table.title }}</span> <span>{{ table.title }} <span class="text-8px">({{bases.get(table.base_id)?.title }})</span></span>
</NcTooltip> </NcTooltip>
</div> </div>
</a-select-option> </a-select-option>
@ -183,14 +231,14 @@ const onModelIdChange =async (modelId:string) =>{
dropdown-class-name="nc-dropdown-ltar-child-table" dropdown-class-name="nc-dropdown-ltar-child-table"
@change="onModelIdChange(vModel.custom.junc_model_id)" @change="onModelIdChange(vModel.custom.junc_model_id)"
> >
<a-select-option v-for="table of tables" :key="table.title" :value="table.id"> <a-select-option v-for="table of refTables" :key="table.title" :value="table.id">
<div class="flex w-full items-center gap-2"> <div class="flex w-full items-center gap-2">
<div class="min-w-5 flex items-center justify-center"> <div class="min-w-5 flex items-center justify-center">
<GeneralTableIcon :meta="table" class="text-gray-500" /> <GeneralTableIcon :meta="table" class="text-gray-500" />
</div> </div>
<NcTooltip class="flex-1 truncate" show-on-truncate-only> <NcTooltip class="flex-1 truncate" show-on-truncate-only>
<template #title>{{ table.title }}</template> <template #title>{{ table.title }}</template>
<span>{{ table.title }}</span> <span>{{ table.title }} <span class="text-8px">({{bases.get(table.base_id)?.title }})</span></span>
</NcTooltip> </NcTooltip>
</div> </div>
</a-select-option> </a-select-option>

2
packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue

@ -65,7 +65,7 @@ type FilterType = keyof typeof checkTypeFunctions
const { sqlUis } = storeToRefs(useBase()) const { sqlUis } = storeToRefs(useBase())
const sqlUi = ref(column.value?.source_id ? sqlUis.value[column.value?.source_id] : Object.values(sqlUis.value)[0]) const sqlUi = ref(column.value?.source_id && sqlUis.value[column.value?.source_id] ? sqlUis.value[column.value?.source_id] : Object.values(sqlUis.value)[0])
const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value)) const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value))

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

@ -1713,13 +1713,14 @@ class BaseModelSqlv2 {
)) as LinkToAnotherRecordColumn )) as LinkToAnotherRecordColumn
).getParentColumn(this.context); ).getParentColumn(this.context);
const parentTable = await parentCol.getModel(this.context); const parentTable = await parentCol.getModel(this.context);
const childModel = await Model.getBaseModelSQL(this.context, { const childBaseModel = await Model.getBaseModelSQL(this.context, {
model: childTable, model: childTable,
dbDriver: this.dbDriver, dbDriver: this.dbDriver,
}); });
await parentTable.getColumns(this.context); await parentTable.getColumns(this.context);
const childTn = this.getTnPath(childTable);
const childTn = childBaseModel.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable); const parentTn = this.getTnPath(parentTable);
const qb = this.dbDriver(childTn); const qb = this.dbDriver(childTn);
@ -1736,7 +1737,7 @@ class BaseModelSqlv2 {
qb.limit(+rest?.limit || 25); qb.limit(+rest?.limit || 25);
qb.offset(+rest?.offset || 0); qb.offset(+rest?.offset || 0);
await childModel.selectObject({ await childBaseModel.selectObject({
qb, qb,
fieldsSet: args.fieldSet, fieldsSet: args.fieldSet,
}); });
@ -1790,7 +1791,11 @@ class BaseModelSqlv2 {
const parentTable = await parentCol.getModel(this.context); const parentTable = await parentCol.getModel(this.context);
await parentTable.getColumns(this.context); await parentTable.getColumns(this.context);
const childTn = this.getTnPath(childTable); const childBaseModel = await Model.getBaseModelSQL({
dbDriver: this.dbDriver,
model: childTable,
});
const childTn = childBaseModel.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable); const parentTn = this.getTnPath(parentTable);
const query = this.dbDriver(childTn) const query = this.dbDriver(childTn)
@ -1945,7 +1950,12 @@ class BaseModelSqlv2 {
)) as LinkToAnotherRecordColumn; )) as LinkToAnotherRecordColumn;
const mmTable = await relColOptions.getMMModel(this.context); const mmTable = await relColOptions.getMMModel(this.context);
const vtn = this.getTnPath(mmTable); const assocBaseModel = await Model.getBaseModelSQL({
id: mmTable.id,
dbDriver: this.dbDriver,
});
const vtn = assocBaseModel.getTnPath(mmTable);
const vcn = (await relColOptions.getMMChildColumn(this.context)) const vcn = (await relColOptions.getMMChildColumn(this.context))
.column_name; .column_name;
const vrcn = (await relColOptions.getMMParentColumn(this.context)) const vrcn = (await relColOptions.getMMParentColumn(this.context))
@ -1980,8 +1990,12 @@ class BaseModelSqlv2 {
).getModel(this.context); ).getModel(this.context);
await parentTable.getColumns(this.context); await parentTable.getColumns(this.context);
const childTn = this.getTnPath(childTable); const parentBaseModel = await Model.getBaseModelSQL({
const parentTn = this.getTnPath(parentTable); id: parentTable.id,
dbDriver: this.dbDriver,
});
const childTn = childBaseModel.getTnPath(childTable);
const parentTn = parentBaseModel.getTnPath(parentTable);
const rtn = childTn; const rtn = childTn;
const qb = this.dbDriver(rtn) const qb = this.dbDriver(rtn)
@ -2002,7 +2016,7 @@ class BaseModelSqlv2 {
).orWhereNull(rcn); ).orWhereNull(rcn);
}); });
const aliasColObjMap = await childTable.getAliasColObjMap(this.context); const aliasColObjMap = await childTable.getAliasColObjMap(this.context);
const filterObj = extractFilterFromXwhere(where, aliasColObjMap); const filterObj = extractFilterFromXwhere(where, aliasColObjMap);
await this.getCustomConditionsAndApply({ await this.getCustomConditionsAndApply({
@ -2096,7 +2110,13 @@ class BaseModelSqlv2 {
)) as LinkToAnotherRecordColumn; )) as LinkToAnotherRecordColumn;
const mmTable = await relColOptions.getMMModel(this.context); const mmTable = await relColOptions.getMMModel(this.context);
const vtn = this.getTnPath(mmTable);
const assocBaseModel = await Model.getBaseModelSQL({
model: mmTable,
dbDriver: this.dbDriver,
});
const vtn = assocBaseModel.getTnPath(mmTable);
const vcn = (await relColOptions.getMMChildColumn(this.context)) const vcn = (await relColOptions.getMMChildColumn(this.context))
.column_name; .column_name;
const vrcn = (await relColOptions.getMMParentColumn(this.context)) const vrcn = (await relColOptions.getMMParentColumn(this.context))
@ -2112,7 +2132,12 @@ class BaseModelSqlv2 {
).getModel(this.context); ).getModel(this.context);
await parentTable.getColumns(this.context); await parentTable.getColumns(this.context);
const childTn = this.getTnPath(childTable); const childBaseModel = await Model.getBaseModelSQL({
dbDriver: this.dbDriver,
model: childTable,
});
const childTn = childBaseModel.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable); const parentTn = this.getTnPath(parentTable);
const rtn = childTn; const rtn = childTn;
@ -2162,20 +2187,33 @@ class BaseModelSqlv2 {
)) as LinkToAnotherRecordColumn; )) as LinkToAnotherRecordColumn;
const mmTable = await relColOptions.getMMModel(this.context); const mmTable = await relColOptions.getMMModel(this.context);
const vtn = this.getTnPath(mmTable); const assocBaseModel = await Model.getBaseModelSQL({
id: mmTable.id,
dbDriver: this.dbDriver,
});
const vtn = assocBaseModel.getTnPath(mmTable);
const vcn = (await relColOptions.getMMChildColumn(this.context)) const vcn = (await relColOptions.getMMChildColumn(this.context))
.column_name; .column_name;
const vrcn = (await relColOptions.getMMParentColumn(this.context)) const vrcn = (await relColOptions.getMMParentColumn(this.context))
.column_name; .column_name;
const rcn = (await relColOptions.getParentColumn(this.context)).column_name; const rcn = (await relColOptions.getParentColumn(this.context)).column_name;
const cn = (await relColOptions.getChildColumn(this.context)).column_name; const cn = (await relColOptions.getChildColumn(this.context)).column_name;
const childTable = await (
await relColOptions.getParentColumn(this.context) const childTable = await (await relColOptions.getParentColumn(this.cotext)).getModel(this.cotext);
).getModel(this.context); const parentTable = await (await relColOptions.getChildColumn(this.cotext)).getModel(this.cotext);
const childModel = await Model.getBaseModelSQL(this.context, { await parentTable.getColumns();
const parentBaseModel = await Model.getBaseModelSQL({
id: parentTable.id,
dbDriver: this.dbDriver, dbDriver: this.dbDriver,
model: childTable,
}); });
const childBaseModel = await Model.getBaseModelSQL({
dbDriver: this.dbDriver,
id: childTable.id,
});
const childTn = childBaseModel.getTnPath(childTable);
const parentTn = parentBaseModel.getTnPath(parentTable);
const childView = await relColOptions.getChildView(this.context); const childView = await relColOptions.getChildView(this.context);
let listArgs: any = {}; let listArgs: any = {};
if (childView) { if (childView) {
@ -2188,14 +2226,6 @@ class BaseModelSqlv2 {
listArgs = dependencyFields; listArgs = dependencyFields;
} }
const parentTable = await (
await relColOptions.getChildColumn(this.context)
).getModel(this.context);
await parentTable.getColumns(this.context);
const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable);
const rtn = childTn; const rtn = childTn;
const qb = this.dbDriver(rtn).where((qb) => const qb = this.dbDriver(rtn).where((qb) =>
@ -2288,8 +2318,8 @@ class BaseModelSqlv2 {
const childView = await relColOptions.getChildView(this.context); const childView = await relColOptions.getChildView(this.context);
const childTn = this.getTnPath(childTable); const childTn = childBaseModel.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable); const parentTn = parentBaseModel.getTnPath(parentTable);
const tn = childTn; const tn = childTn;
const rtn = parentTn; const rtn = parentTn;
@ -2308,7 +2338,7 @@ class BaseModelSqlv2 {
await this.shuffle({ qb }); await this.shuffle({ qb });
} }
await childModel.selectObject({ qb }); await childBaseModel.selectObject({ qb });
const aliasColObjMap = await childTable.getAliasColObjMap(this.context); const aliasColObjMap = await childTable.getAliasColObjMap(this.context);
const filterObj = extractFilterFromXwhere(where, aliasColObjMap); const filterObj = extractFilterFromXwhere(where, aliasColObjMap);
@ -2330,12 +2360,11 @@ class BaseModelSqlv2 {
applyPaginate(qb, rest); applyPaginate(qb, rest);
const proto = await childModel.getProto(); const proto = await childBaseModel.getProto();
const data = await this.execAndParse( const data = await this.execAndParse(
qb, qb,
await childTable.getColumns(this.context), await childTable.getColumns(this.context),
); );
return data.map((c) => { return data.map((c) => {
c.__proto__ = proto; c.__proto__ = proto;
return c; return c;
@ -2367,7 +2396,12 @@ class BaseModelSqlv2 {
const childView = await relColOptions.getChildView(this.context); const childView = await relColOptions.getChildView(this.context);
const childTn = this.getTnPath(childTable); const childBaseModel = await Model.getBaseModelSQL({
dbDriver: this.dbDriver,
model: childTable,
});
const childTn = childBaseModel.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable); const parentTn = this.getTnPath(parentTable);
const tn = childTn; const tn = childTn;
@ -2431,7 +2465,12 @@ class BaseModelSqlv2 {
dbDriver: this.dbDriver, dbDriver: this.dbDriver,
model: childTable, model: childTable,
}); });
const childBaseModel = await Model.getBaseModelSQL({
dbDriver: this.dbDriver,
model: childTable,
});
const childTn = childBaseModel.getTnPath(childTable);
const childView = await relColOptions.getChildView(this.context); const childView = await relColOptions.getChildView(this.context);
let listArgs: any = {}; let listArgs: any = {};
if (childView) { if (childView) {
@ -2530,8 +2569,13 @@ class BaseModelSqlv2 {
await relColOptions.getChildColumn(this.context) await relColOptions.getChildColumn(this.context)
).getModel(this.context); ).getModel(this.context);
const parentBaseModel = await Model.getBaseModelSQL({
dbDriver: this.dbDriver,
model: parentTable,
});
const childTn = this.getTnPath(childTable); const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable); const parentTn = parentBaseModel.getTnPath(parentTable);
const rtn = parentTn; const rtn = parentTn;
const tn = childTn; const tn = childTn;
@ -2652,13 +2696,13 @@ class BaseModelSqlv2 {
const childTable = await ( const childTable = await (
await relColOptions.getChildColumn(this.context) await relColOptions.getChildColumn(this.context)
).getModel(this.context); ).getModel(this.context);
const parentModel = await Model.getBaseModelSQL(this.context, { const parentBaseModel = await Model.getBaseModelSQL(this.context, {
dbDriver: this.dbDriver, dbDriver: this.dbDriver,
model: parentTable, model: parentTable,
}); });
const childTn = this.getTnPath(childTable); const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable); const parentTn = parentBaseModel.getTnPath(parentTable);
const rtn = parentTn; const rtn = parentTn;
const tn = childTn; const tn = childTn;
@ -2679,7 +2723,7 @@ class BaseModelSqlv2 {
await this.shuffle({ qb }); await this.shuffle({ qb });
} }
await parentModel.selectObject({ qb }); await parentBaseModel.selectObject({ qb });
const aliasColObjMap = await parentTable.getAliasColObjMap(this.context); const aliasColObjMap = await parentTable.getAliasColObjMap(this.context);
const filterObj = extractFilterFromXwhere(where, aliasColObjMap); const filterObj = extractFilterFromXwhere(where, aliasColObjMap);
@ -2706,7 +2750,7 @@ class BaseModelSqlv2 {
applyPaginate(qb, rest); applyPaginate(qb, rest);
const proto = await parentModel.getProto(); const proto = await parentBaseModel.getProto();
const data = await this.execAndParse( const data = await this.execAndParse(
qb, qb,
await parentTable.getColumns(this.context), await parentTable.getColumns(this.context),
@ -5525,8 +5569,19 @@ class BaseModelSqlv2 {
await childTable.getColumns(this.context); await childTable.getColumns(this.context);
await parentTable.getColumns(this.context); await parentTable.getColumns(this.context);
const childTn = this.getTnPath(childTable); const parentBaseModel = await Model.getBaseModelSQL({
const parentTn = this.getTnPath(parentTable); model: parentTable,
dbDriver: this.dbDriver,
});
const childBaseModel = await Model.getBaseModelSQL({
dbDriver: this.dbDriver,
model: childTable,
});
const childTn = childBaseModel.getTnPath(childTable);
const parentTn = parentBaseModel.getTnPath(parentTable);
const relatedChildCol = getRelatedLinksColumn( const relatedChildCol = getRelatedLinksColumn(
column, column,
@ -5582,15 +5637,20 @@ class BaseModelSqlv2 {
const vParentCol = await colOptions.getMMParentColumn(this.context); const vParentCol = await colOptions.getMMParentColumn(this.context);
const vTable = await colOptions.getMMModel(this.context); const vTable = await colOptions.getMMModel(this.context);
const vTn = this.getTnPath(vTable); const assocBaseModel = await Model.getBaseModelSQL({
model: vTable,
dbDriver: this.dbDriver,
});
const vTn = assocBaseModel.getTnPath(vTable);
if (this.isSnowflake || this.isDatabricks) { if (this.isSnowflake || this.isDatabricks) {
const parentPK = this.dbDriver(parentTn) const parentPK = parentBaseModel.dbDriver(parentTn)
.select(parentColumn.column_name) .select(parentColumn.column_name)
.where(_wherePk(parentTable.primaryKeys, childId)) .where(_wherePk(parentTable.primaryKeys, childId))
.first(); .first();
const childPK = this.dbDriver(childTn) const childPK = childBaseModel.dbDriver(childTn)
.select(childColumn.column_name) .select(childColumn.column_name)
.where(_wherePk(childTable.primaryKeys, rowId)) .where(_wherePk(childTable.primaryKeys, rowId))
.first(); .first();
@ -5621,11 +5681,13 @@ class BaseModelSqlv2 {
} }
await this.updateLastModified({ await this.updateLastModified({
baseModel: parentBaseModel,
model: parentTable, model: parentTable,
rowIds: [childId], rowIds: [childId],
cookie, cookie,
}); });
await this.updateLastModified({ await this.updateLastModified({
baseModel: childBaseModel,
model: childTable, model: childTable,
rowIds: [rowId], rowIds: [rowId],
cookie, cookie,
@ -5699,6 +5761,7 @@ class BaseModelSqlv2 {
await triggerAfterRemoveChild(); await triggerAfterRemoveChild();
await this.updateLastModified({ await this.updateLastModified({
baseModel: parentBaseModel,
model: parentTable, model: parentTable,
rowIds: [rowId], rowIds: [rowId],
cookie, cookie,
@ -5811,6 +5874,7 @@ class BaseModelSqlv2 {
await triggerAfterRemoveChild(); await triggerAfterRemoveChild();
await this.updateLastModified({ await this.updateLastModified({
baseModel: parentBaseModel,
model: parentTable, model: parentTable,
rowIds: [childId], rowIds: [childId],
cookie, cookie,
@ -6060,6 +6124,7 @@ class BaseModelSqlv2 {
); );
await this.updateLastModified({ await this.updateLastModified({
baseModel: parentBaseModel,
model: parentTable, model: parentTable,
rowIds: [childId], rowIds: [childId],
cookie, cookie,
@ -6176,8 +6241,18 @@ class BaseModelSqlv2 {
await childTable.getColumns(this.context); await childTable.getColumns(this.context);
await parentTable.getColumns(this.context); await parentTable.getColumns(this.context);
const childTn = this.getTnPath(childTable); const parentBaseModel = await Model.getBaseModelSQL({
const parentTn = this.getTnPath(parentTable); model: parentTable,
dbDriver: this.dbDriver,
});
const childBaseModel = await Model.getBaseModelSQL({
dbDriver: this.dbDriver,
model: childTable,
});
const childTn = childBaseModel.getTnPath(childTable);
const parentTn = parentBaseModel.getTnPath(parentTable);
const relatedChildCol = getRelatedLinksColumn( const relatedChildCol = getRelatedLinksColumn(
column, column,
@ -6210,8 +6285,11 @@ class BaseModelSqlv2 {
const vChildCol = await colOptions.getMMChildColumn(this.context); const vChildCol = await colOptions.getMMChildColumn(this.context);
const vParentCol = await colOptions.getMMParentColumn(this.context); const vParentCol = await colOptions.getMMParentColumn(this.context);
const vTable = await colOptions.getMMModel(this.context); const vTable = await colOptions.getMMModel(this.context);
const assocBaseModel = await Model.getBaseModelSQL({
const vTn = this.getTnPath(vTable); model: vTable,
dbDriver: this.dbDriver,
});
const vTn = assocBaseModel.getTnPath(vTable);
await this.execAndParse( await this.execAndParse(
this.dbDriver(vTn) this.dbDriver(vTn)
@ -6231,11 +6309,13 @@ class BaseModelSqlv2 {
); );
await this.updateLastModified({ await this.updateLastModified({
baseModel: parentBaseModel,
model: parentTable, model: parentTable,
rowIds: [childId], rowIds: [childId],
cookie, cookie,
}); });
await this.updateLastModified({ await this.updateLastModified({
baseModel: childBaseModel,
model: childTable, model: childTable,
rowIds: [rowId], rowIds: [rowId],
cookie, cookie,
@ -6264,6 +6344,7 @@ class BaseModelSqlv2 {
); );
await this.updateLastModified({ await this.updateLastModified({
baseModel: parentBaseModel,
model: parentTable, model: parentTable,
rowIds: [rowId], rowIds: [rowId],
cookie, cookie,
@ -6290,6 +6371,7 @@ class BaseModelSqlv2 {
); );
await this.updateLastModified({ await this.updateLastModified({
baseModel: parentBaseModel,
model: parentTable, model: parentTable,
rowIds: [childId], rowIds: [childId],
cookie, cookie,
@ -6312,6 +6394,7 @@ class BaseModelSqlv2 {
); );
await this.updateLastModified({ await this.updateLastModified({
baseModel: parentBaseModel,
model: parentTable, model: parentTable,
rowIds: [childId], rowIds: [childId],
cookie, cookie,
@ -8027,11 +8110,13 @@ class BaseModelSqlv2 {
cookie, cookie,
model = this.model, model = this.model,
knex = this.dbDriver, knex = this.dbDriver,
baseModel = this,
}: { }: {
rowIds: any | any[]; rowIds: any | any[];
cookie?: { user?: any }; cookie?: { user?: any };
model?: Model; model?: Model;
knex?: XKnex; knex?: XKnex;
baseModel?: BaseModelSqlv2
}) { }) {
const columns = await model.getColumns(this.context); const columns = await model.getColumns(this.context);
@ -8055,7 +8140,7 @@ class BaseModelSqlv2 {
if (Object.keys(updateObject).length === 0) return; if (Object.keys(updateObject).length === 0) return;
const qb = knex(this.getTnPath(model.table_name)).update(updateObject); const qb = knex(baseModel.getTnPath(model.table_name)).update(updateObject);
for (const rowId of Array.isArray(rowIds) ? rowIds : [rowIds]) { for (const rowId of Array.isArray(rowIds) ? rowIds : [rowIds]) {
qb.orWhere(_wherePk(model.primaryKeys, rowId)); qb.orWhere(_wherePk(model.primaryKeys, rowId));

33
packages/nocodb/src/db/genRollupSelectv2.ts

@ -7,6 +7,7 @@ import type {
} from '~/models'; } from '~/models';
import type { XKnex } from '~/db/CustomKnex'; import type { XKnex } from '~/db/CustomKnex';
import type { Knex } from 'knex'; import type { Knex } from 'knex';
import { Model } from '~/models';
export default async function ({ export default async function ({
baseModelSqlv2, baseModelSqlv2,
@ -33,11 +34,20 @@ export default async function ({
const parentModel = await parentCol?.getModel(context); const parentModel = await parentCol?.getModel(context);
const refTableAlias = `__nc_rollup`; const refTableAlias = `__nc_rollup`;
const parentBaseModel = await Model.getBaseModelSQL({
model: parentModel,
dbDriver: knex,
});
const childBaseModel = await Model.getBaseModelSQL({
model: childModel,
dbDriver: knex,
});
switch (relationColumnOption.type) { switch (relationColumnOption.type) {
case RelationTypes.HAS_MANY: { case RelationTypes.HAS_MANY: {
const queryBuilder: any = knex( const queryBuilder: any = knex(
knex.raw(`?? as ??`, [ knex.raw(`?? as ??`, [
baseModelSqlv2.getTnPath(childModel?.table_name), childBaseModel.getTnPath(childModel),
refTableAlias, refTableAlias,
]), ]),
) )
@ -46,7 +56,7 @@ export default async function ({
) )
.where( .where(
knex.ref( knex.ref(
`${alias || baseModelSqlv2.getTnPath(parentModel.table_name)}.${ `${alias || parentBaseModel.getTnPath(parentModel.table_name)}.${
parentCol.column_name parentCol.column_name
}`, }`,
), ),
@ -62,7 +72,7 @@ export default async function ({
case RelationTypes.ONE_TO_ONE: { case RelationTypes.ONE_TO_ONE: {
const qb = knex( const qb = knex(
knex.raw(`?? as ??`, [ knex.raw(`?? as ??`, [
baseModelSqlv2.getTnPath(childModel?.table_name), childBaseModel.getTnPath(childModel?.table_name),
refTableAlias, refTableAlias,
]), ]),
) )
@ -71,7 +81,7 @@ export default async function ({
) )
.where( .where(
knex.ref( knex.ref(
`${alias || baseModelSqlv2.getTnPath(parentModel.table_name)}.${ `${alias || parentBaseModel.getTnPath(parentModel.table_name)}.${
parentCol.column_name parentCol.column_name
}`, }`,
), ),
@ -88,7 +98,10 @@ export default async function ({
const mmModel = await relationColumnOption.getMMModel(context); const mmModel = await relationColumnOption.getMMModel(context);
const mmChildCol = await relationColumnOption.getMMChildColumn(context); const mmChildCol = await relationColumnOption.getMMChildColumn(context);
const mmParentCol = await relationColumnOption.getMMParentColumn(context); const mmParentCol = await relationColumnOption.getMMParentColumn(context);
const assocBaseModel = await Model.getBaseModelSQL({
id: mmModel.id,
dbDriver: knex,
});
if (!mmModel) { if (!mmModel) {
return this.dbDriver.raw(`?`, [ return this.dbDriver.raw(`?`, [
NcDataErrorCodes.NC_ERR_MM_MODEL_NOT_FOUND, NcDataErrorCodes.NC_ERR_MM_MODEL_NOT_FOUND,
@ -97,7 +110,7 @@ export default async function ({
const qb = knex( const qb = knex(
knex.raw(`?? as ??`, [ knex.raw(`?? as ??`, [
baseModelSqlv2.getTnPath(parentModel?.table_name), parentBaseModel.getTnPath(parentModel?.table_name),
refTableAlias, refTableAlias,
]), ]),
) )
@ -105,9 +118,9 @@ export default async function ({
knex.ref(`${refTableAlias}.${rollupColumn.column_name}`), knex.ref(`${refTableAlias}.${rollupColumn.column_name}`),
) )
.innerJoin( .innerJoin(
baseModelSqlv2.getTnPath(mmModel.table_name), assocBaseModel.getTnPath(mmModel.table_name),
knex.ref( knex.ref(
`${baseModelSqlv2.getTnPath(mmModel.table_name)}.${ `${assocBaseModel.getTnPath(mmModel.table_name)}.${
mmParentCol.column_name mmParentCol.column_name
}`, }`,
), ),
@ -116,13 +129,13 @@ export default async function ({
) )
.where( .where(
knex.ref( knex.ref(
`${baseModelSqlv2.getTnPath(mmModel.table_name)}.${ `${assocBaseModel.getTnPath(mmModel.table_name)}.${
mmChildCol.column_name mmChildCol.column_name
}`, }`,
), ),
'=', '=',
knex.ref( knex.ref(
`${alias || baseModelSqlv2.getTnPath(childModel.table_name)}.${ `${alias || childBaseModel.getTnPath(childModel.table_name)}.${
childCol.column_name childCol.column_name
}`, }`,
), ),

12
packages/nocodb/src/services/columns.service.ts

@ -2472,9 +2472,8 @@ export class ColumnsService {
}, },
ignoreFkDelete = false, ignoreFkDelete = false,
) => { ) => {
if (childTable) { if (childTable && !custom) {
if (!custom) { let foreignKeyName;
let foreignKeyName;
// if relationColOpt is not provided, extract it from child table // if relationColOpt is not provided, extract it from child table
// and get the foreign key name for dropping the foreign key // and get the foreign key name for dropping the foreign key
@ -2485,10 +2484,8 @@ export class ColumnsService {
for (const col of cols) { for (const col of cols) {
if (col.uidt === UITypes.LinkToAnotherRecord) { if (col.uidt === UITypes.LinkToAnotherRecord) {
const colOptions = const colOptions =
await col.getColOptions<LinkToAnotherRecordColumn>( await col.getColOptions<LinkToAnotherRecordColumn>(context,
context, ncMeta);
ncMeta,
);
if (colOptions.fk_related_model_id === parentTable.id) { if (colOptions.fk_related_model_id === parentTable.id) {
return { colOptions }; return { colOptions };
} }
@ -2517,7 +2514,6 @@ export class ColumnsService {
} }
} }
} }
}
if (!relationColOpt) return; if (!relationColOpt) return;
const columnsInRelatedTable: Column[] = await relationColOpt const columnsInRelatedTable: Column[] = await relationColOpt

1
packages/nocodb/tsconfig.json

@ -41,5 +41,4 @@
] ]
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["src/ee", "src/ee-on-prem"]
} }

Loading…
Cancel
Save