Browse Source

feat: M2m

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/341/head
Pranav C 3 years ago
parent
commit
3af94bbb72
  1. 1
      packages/nc-gui/components/project/spreadsheet/apis/restApi.js
  2. 1
      packages/nc-gui/components/project/spreadsheet/components/editColumn.vue
  3. 124
      packages/nc-gui/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions.vue
  4. 9
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItems.vue
  5. 91
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue
  6. 5
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue
  7. 10
      packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js
  8. 13
      packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue
  9. 7
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  10. 1
      packages/nc-gui/store/sqlMgr.js
  11. 7
      packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts
  12. 3
      packages/nocodb/src/lib/noco/NcProjectBuilder.ts
  13. 131
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  14. 173
      packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts
  15. 33
      packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts
  16. 86
      packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts
  17. 459
      packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXc.ts
  18. 9
      packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMssql.ts
  19. 77
      packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMysql.ts
  20. 9
      packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaOracle.ts
  21. 10
      packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaPg.ts
  22. 9
      packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaSqlite.ts

1
packages/nc-gui/components/project/spreadsheet/apis/restApi.js

@ -60,7 +60,6 @@ export default class RestApi {
// const list = await this.list(params); // const list = await this.list(params);
// const count = (await this.count({where: params.where || ''})).count; // const count = (await this.count({where: params.where || ''})).count;
const {list, info: {count}} = (await this.get(`/nc/${this.$ctx.$route.params.project_id}/api/v1/${this.table}/m2mNotChildren/${assoc}/${pid}`, params)).data const {list, info: {count}} = (await this.get(`/nc/${this.$ctx.$route.params.project_id}/api/v1/${this.table}/m2mNotChildren/${assoc}/${pid}`, params)).data
debugger
return {list, count}; return {list, count};
} }

1
packages/nc-gui/components/project/spreadsheet/components/editColumn.vue

@ -89,6 +89,7 @@
:column="newColumn" :column="newColumn"
:nodes="nodes" :nodes="nodes"
:meta="meta" :meta="meta"
:isSQLite="isSQLite"
@onColumnSelect="onRelColumnSelect" @onColumnSelect="onRelColumnSelect"
></linked-to-another-options> ></linked-to-another-options>
</v-col> </v-col>

124
packages/nc-gui/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions.vue

@ -5,7 +5,7 @@
<v-col> <v-col>
<v-radio-group row hide-details dense v-model="type" class="pt-0 mt-0"> <v-radio-group row hide-details dense v-model="type" class="pt-0 mt-0">
<v-radio value="hm" label="Has Many"></v-radio> <v-radio value="hm" label="Has Many"></v-radio>
<v-radio disabled value="mm" label="Many To Many"></v-radio> <v-radio value="mm" label="Many To Many"></v-radio>
<v-radio disabled value="oo" label="One To One"></v-radio> <v-radio disabled value="oo" label="One To One"></v-radio>
</v-radio-group> </v-radio-group>
</v-col> </v-col>
@ -47,55 +47,55 @@
</v-col </v-col
> >
</v-row> </v-row>
<template v-if="!isSQLite">
<v-row> <v-row>
<v-col cols="6"> <v-col cols="6">
<v-autocomplete <v-autocomplete
outlined outlined
class="caption" class="caption"
hide-details hide-details
label="On Update" label="On Update"
:full-width="false" :full-width="false"
v-model="relation.onUpdate" v-model="relation.onUpdate"
:items="onUpdateDeleteOptions" :items="onUpdateDeleteOptions"
required required
dense dense
:disabled="relation.type !== 'real'" :disabled="relation.type !== 'real'"
></v-autocomplete> ></v-autocomplete>
</v-col> </v-col>
<v-col cols="6"> <v-col cols="6">
<v-autocomplete <v-autocomplete
outlined outlined
class="caption" class="caption"
hide-details hide-details
label="On Delete" label="On Delete"
:full-width="false" :full-width="false"
v-model="relation.onDelete" v-model="relation.onDelete"
:items="onUpdateDeleteOptions" :items="onUpdateDeleteOptions"
required required
dense dense
:disabled="relation.type !== 'real'" :disabled="relation.type !== 'real'"
></v-autocomplete> ></v-autocomplete>
</v-col> </v-col>
</v-row> </v-row>
<v-row> <v-row>
<v-col>
<v-checkbox
false-value="real"
true-value="virtual"
label="Virtual Relation"
:full-width="false"
v-model="relation.type"
required
class="mt-0"
dense
></v-checkbox>
</v-col>
</v-row>
<v-col>
<v-checkbox
false-value="real"
true-value="virtual"
label="Virtual Relation"
:full-width="false"
v-model="relation.type"
required
class="mt-0"
dense
></v-checkbox>
</v-col>
</v-row>
</template>
</v-container> </v-container>
</div> </div>
</template> </template>
@ -103,7 +103,7 @@
<script> <script>
export default { export default {
name: "linked-to-another-options", name: "linked-to-another-options",
props: ['nodes', 'column', 'meta'], props: ['nodes', 'column', 'meta', 'isSQLite'],
data: () => ({ data: () => ({
type: 'hm', type: 'hm',
refTables: [], refTables: [],
@ -170,8 +170,30 @@ export default {
this.refTables = result.data.list.map(({tn, _tn}) => ({tn, _tn})) this.refTables = result.data.list.map(({tn, _tn}) => ({tn, _tn}))
this.isRefTablesLoading = false; this.isRefTablesLoading = false;
}, },
async saveManyToMany() {
try {
await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [
{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
},
'xcM2MRelationCreate',
{
...this.relation,
type: this.isSQLite || this.relation.type === 'virtual' ? 'virtual' : 'real',
parentTable: this.meta.tn,
updateRelation: this.column.rtn ? true : false
}
]);
} catch (e) {
throw e
}
},
async saveRelation() { async saveRelation() {
if (this.type === 'mm') {
await this.saveManyToMany();
return;
}
try { try {
const parentPK = this.meta.columns.find(c => c.pk); const parentPK = this.meta.columns.find(c => c.pk);
@ -220,7 +242,7 @@ export default {
env: this.nodes.env, env: this.nodes.env,
dbAlias: this.nodes.dbAlias dbAlias: this.nodes.dbAlias
}, },
this.relation.type === 'real' ? "relationCreate" : 'xcVirtualRelationCreate', this.relation.type === 'real' && !this.isSQLite ? "relationCreate" : 'xcVirtualRelationCreate',
{ {
...this.relation, ...this.relation,
parentTable: this.meta.tn, parentTable: this.meta.tn,
@ -230,7 +252,7 @@ export default {
} }
]); ]);
} catch (e) { } catch (e) {
console.log(e.message) throw e
} }
}, },
onColumnSelect() { onColumnSelect() {

9
packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItems.vue

@ -1,18 +1,17 @@
<template> <template>
<v-dialog v-model="value" width="600"> <v-dialog v-model="show" width="600">
<v-card width="600" color="backgroundColor"> <v-card width="600" color="backgroundColor">
<v-card-title class="textColor--text mx-2">{{ meta ? meta._tn : 'Children' }} <v-card-title class="textColor--text mx-2">{{ meta ? meta._tn : 'Children' }}
<v-spacer> <v-spacer>
</v-spacer> </v-spacer>
<v-btn small class="caption" color="primary" @click="emit('new-record')"> <v-btn small class="caption" color="primary" @click="$emit('new-record')">
<v-icon small>mdi-plus</v-icon>&nbsp; <v-icon small>mdi-plus</v-icon>&nbsp;
Add Record Add Record
</v-btn> </v-btn>
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
<div class="items-container"> <div class="items-container">
<template v-if="data && data.list"> <template v-if="data && data.list">
<v-card <v-card
@ -32,6 +31,7 @@
>mdi-link-variant-remove >mdi-link-variant-remove
</x-icon> </x-icon>
<x-icon <x-icon
v-if="!mm"
:tooltip="`Delete row in '${meta._tn}'`" :tooltip="`Delete row in '${meta._tn}'`"
:color="['error','grey']" :color="['error','grey']"
small small
@ -87,6 +87,7 @@ export default {
parentMeta: Object, parentMeta: Object,
size: Number, size: Number,
api: [Object, Function], api: [Object, Function],
mm:[Object, Boolean]
}, },
data: () => ({ data: () => ({
data: null, data: null,
@ -99,7 +100,7 @@ export default {
async loadData() { async loadData() {
if (!this.api) return; if (!this.api) return;
this.data = await this.api.paginatedList({ this.data = await this.api.paginatedList({
limit: this.size, limit: this.size,
offset: this.size * (this.page - 1), offset: this.size * (this.page - 1),
...this.queryParams ...this.queryParams

91
packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue

@ -35,23 +35,28 @@
where: `~not(${childForeignKey},eq,${parentId})~or(${childForeignKey},is,null)`, where: `~not(${childForeignKey},eq,${parentId})~or(${childForeignKey},is,null)`,
}"/> }"/>
<!-- <list-child-items <list-child-items
v-if="childListModal" ref="childList"
v-model="childListModal" v-if="childListModal"
:size="10" v-model="childListModal"
:meta="childMeta" :size="10"
:parent-meta="meta" :meta="childMeta"
:primary-col="childPrimaryCol" :parent-meta="meta"
:primary-key="childPrimaryKey" :primary-col="childPrimaryCol"
:api="childApi" :primary-key="childPrimaryKey"
:query-params="childQueryParams" :api="childApi"
@new-record="showNewRecordModal" :query-params="{
@edit="editChild" ...childQueryParams,
@unlink="unlinkChild" where: `(${childForeignKey},eq,${parentId})`
/>--> }"
@new-record="showNewRecordModal"
@edit="editChild"
@unlink="unlinkChild"
@delete="deleteChild"
/>
<v-dialog v-if="childListModal" v-model="childListModal" width="600"> <!--<v-dialog v-if="childListModal" v-model="childListModal" width="600">
<v-card width="600" color="backgroundColor"> <v-card width="600" color="backgroundColor">
<v-card-title class="textColor&#45;&#45;text mx-2">{{ childMeta ? childMeta._tn : 'Children' }} <v-card-title class="textColor&#45;&#45;text mx-2">{{ childMeta ? childMeta._tn : 'Children' }}
<v-spacer> <v-spacer>
@ -113,7 +118,7 @@
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
-->
<dlg-label-submit-cancel <dlg-label-submit-cancel
type="primary" type="primary"
@ -194,7 +199,7 @@ export default {
childListModal: false, childListModal: false,
childMeta: null, childMeta: null,
// list: null, // list: null,
childList: null, // childList: null,
dialogShow: false, dialogShow: false,
confirmAction: null, confirmAction: null,
confirmMessage: '', confirmMessage: '',
@ -204,30 +209,30 @@ export default {
// page: 1, // page: 1,
// size: 10 // size: 10
// }, // },
childListPagination: { // childListPagination: {
page: 1, // page: 1,
size: 10 // size: 10
}, // },
isNewChild: false isNewChild: false
}), }),
methods: { methods: {
async showChildListModal() { async showChildListModal() {
this.childListModal = true;
await this.loadChildMeta(); await this.loadChildMeta();
await this.loadChildList(); // await this.loadChildList();
}, this.childListModal = true;
async loadChildList() {
const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn;
this.childList = await this.childApi.paginatedList({
where: `(${_cn},eq,${pid})`,
limit: this.childListPagination.size,
offset: this.childListPagination.size * (this.childListPagination.page - 1),
...this.childQueryParams
})
}, },
// async loadChildList() {
// const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
// const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn;
// this.childList = await this.childApi.paginatedList({
// where: `(${_cn},eq,${pid})`,
// limit: this.childListPagination.size,
// offset: this.childListPagination.size * (this.childListPagination.page - 1),
// ...this.childQueryParams
// })
//
// },
async deleteChild(child) { async deleteChild(child) {
this.dialogShow = true; this.dialogShow = true;
this.confirmMessage = this.confirmMessage =
@ -237,10 +242,16 @@ export default {
this.dialogShow = false; this.dialogShow = false;
} else { } else {
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
await this.childApi.delete(id) try {
this.showChildListModal(); await this.childApi.delete(id)
this.dialogShow = false; this.dialogShow = false;
this.$emit('loadTableData') this.$emit('loadTableData')
if (this.childListModal && this.$refs.childList) {
this.$refs.childList.loadData();
}
}catch (e) {
this.$toast.error(e.message)
}
} }
} }
}, },
@ -262,8 +273,8 @@ export default {
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
await this.childApi.update(id, {[_cn]: null}, child) await this.childApi.update(id, {[_cn]: null}, child)
this.$emit('loadTableData') this.$emit('loadTableData')
if (this.childListModal) { if (this.childListModal && this.$refs.childList) {
this.showChildListModal() this.$refs.childList.loadData();
} }
// } // }
// } // }

5
packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue

@ -38,6 +38,7 @@
:query-params="childQueryParams"/> :query-params="childQueryParams"/>
<list-child-items <list-child-items
ref="childList"
v-if="childListModal" v-if="childListModal"
v-model="childListModal" v-model="childListModal"
:size="10" :size="10"
@ -226,6 +227,10 @@ export default {
const id = this.assocMeta.columns.filter((c) => c.cn === apcn || c.cn === accn).map(c => c.cn === apcn ? this.row[_pcn] : child[_ccn]).join('___'); const id = this.assocMeta.columns.filter((c) => c.cn === apcn || c.cn === accn).map(c => c.cn === apcn ? this.row[_pcn] : child[_ccn]).join('___');
await this.assocApi.delete(id) await this.assocApi.delete(id)
this.$emit('loadTableData') this.$emit('loadTableData')
if (this.childListModal && this.$refs.childList) {
this.$refs.childList.loadData();
// this.showChildListModal()
}
}, },
async removeChild(child) { async removeChild(child) {
this.dialogShow = true; this.dialogShow = true;

10
packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js

@ -67,7 +67,7 @@ export default {
columns = this.meta.columns || []; columns = this.meta.columns || [];
} else if (this.data && this.data.length) { } else if (this.data && this.data.length) {
columns = (this.meta.columns.filter(c => !(c.pk && c.ai) columns = (this.meta.columns.filter(c => !(c.pk && c.ai)
&& !(this.meta.v.some(v => v.bt && v.bt.cn === c.cn)) && !((this.meta.v || []).some(v => v.bt && v.bt.cn === c.cn))
&& !hideCols.includes(c.cn))) || []; && !hideCols.includes(c.cn))) || [];
} else { } else {
columns = (this.meta && this.meta.columns && this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn))) || []; columns = (this.meta && this.meta.columns && this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn))) || [];
@ -111,9 +111,9 @@ export default {
offset: this.size * (this.page - 1), offset: this.size * (this.page - 1),
where: this.concatenatedXWhere, where: this.concatenatedXWhere,
sort: this.sort, sort: this.sort,
childs: (this.meta && this.meta.hasMany && this.meta.hasMany.map(hm => hm.tn).join()) || '', childs: (this.meta && this.meta.v && this.meta.v.filter(v=>v.hm).map(({hm}) => hm.tn).join()) || '',
parents: (this.meta && this.meta.belongsTo && this.meta.belongsTo.map(hm => hm.rtn).join()) || '', parents: (this.meta && this.meta.v && this.meta.v.filter(v=>v.bt).map(({bt}) => bt.rtn).join()) || '',
many: (this.meta && this.meta.manyToMany && this.meta.manyToMany.map(mm => mm.rtn).join()) || '' many: (this.meta && this.meta.v && this.meta.v.filter(v=>v.mm).map(({mm}) => mm.rtn).join()) || ''
} }
}, colLength() { }, colLength() {
return (this.availableColumns && this.availableColumns.length) || 0 return (this.availableColumns && this.availableColumns.length) || 0
@ -140,7 +140,7 @@ export default {
}, },
belongsTo() { belongsTo() {
return this.meta && this.meta.belongsTo ? this.meta.belongsTo.reduce((bt, o) => { return this.meta && this.meta.belongsTo ? this.meta.belongsTo.reduce((bt, o) => {
const _cn = (this.meta.columns.find(c => c.cn === o.cn)||{})._cn const _cn = (this.meta.columns.find(c => c.cn === o.cn) || {})._cn
bt[_cn] = o; bt[_cn] = o;
return bt; return bt;
}, {}) : {}; }, {}) : {};

13
packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue

@ -838,7 +838,7 @@ export default {
break; break;
} }
}, },
async loadMeta() { async loadMeta(updateShowFields = true) {
this.loadingMeta = true; this.loadingMeta = true;
const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env, env: this.nodes.env,
@ -848,6 +848,13 @@ export default {
}]); }]);
this.meta = JSON.parse(tableMeta.meta); this.meta = JSON.parse(tableMeta.meta);
this.loadingMeta = false; this.loadingMeta = false;
if (updateShowFields) {
try {
const qp = JSON.parse(tableMeta.query_params)
this.showFields = qp.showFields ? qp.showFields : this.showFields;
} catch (e) {
}
}
}, },
loadTableDataDeb: debounce(async function (self) { loadTableDataDeb: debounce(async function (self) {
await self.loadTableDataFn() await self.loadTableDataFn()
@ -889,10 +896,10 @@ export default {
this.selectedExpandRowMeta = rowMeta; this.selectedExpandRowMeta = rowMeta;
}, },
async onNewColCreation() { async onNewColCreation() {
await this.loadMeta(); await this.loadMeta(true);
this.$nextTick(async () => { this.$nextTick(async () => {
await this.loadTableData(); await this.loadTableData();
this.mapFieldsAndShowFields(); // this.mapFieldsAndShowFields();
}); });
} }
}, },

7
packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue

@ -1,5 +1,6 @@
<template> <template>
<div> <div>
<table v-if="data" class="xc-row-table" <table v-if="data" class="xc-row-table"
style=" "> style=" ">
<thead> <thead>
@ -414,7 +415,11 @@ export default {
} }
}, },
onClickOutside() { onClickOutside() {
if (this.meta.columns[this.selected.col].virtual) return if (
this.meta.columns
&& this.meta.columns[this.selected.col]
&& this.meta.columns[this.selected.col].virtual
) return
this.selected.col = null; this.selected.col = null;
this.selected.row = null this.selected.row = null
}, },

1
packages/nc-gui/store/sqlMgr.js

@ -253,6 +253,7 @@ function translateUiToLibCall(args, op, opArgs) {
break; break;
case 'relationCreate': case 'relationCreate':
case 'xcM2MRelationCreate':
case 'xcVirtualRelationCreate': case 'xcVirtualRelationCreate':
data.type = "Relation create"; data.type = "Relation create";
data.title = ''; data.title = '';

7
packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts

@ -1150,7 +1150,8 @@ class BaseModelSql extends BaseModel {
let {fields, where, limit, offset, sort} = this._getChildListArgs(rest, index, child); let {fields, where, limit, offset, sort} = this._getChildListArgs(rest, index, child);
const {tn, cn, vtn, vcn, vrcn, rtn, rcn} = this.manyToManyRelations.find(({rtn}) => rtn === child) || {}; const {tn, cn, vtn, vcn, vrcn, rtn, rcn} = this.manyToManyRelations.find(({rtn}) => rtn === child) || {};
const _cn = this.dbModels[tn].columnToAlias?.[cn]; // @ts-ignore
// const _cn = this.dbModels[tn].columnToAlias?.[cn];
if (fields !== '*' && fields.split(',').indexOf(cn) === -1) { if (fields !== '*' && fields.split(',').indexOf(cn) === -1) {
fields += ',' + cn; fields += ',' + cn;
@ -1165,14 +1166,14 @@ class BaseModelSql extends BaseModel {
.join(vtn, `${vtn}.${vrcn}`, `${rtn}.${rcn}`) .join(vtn, `${vtn}.${vrcn}`, `${rtn}.${rcn}`)
.where(`${vtn}.${vcn}`, p[this.columnToAlias?.[this.pks[0].cn] || this.pks[0].cn]) .where(`${vtn}.${vcn}`, p[this.columnToAlias?.[this.pks[0].cn] || this.pks[0].cn])
.xwhere(where, this.dbModels[child].selectQuery('')) .xwhere(where, this.dbModels[child].selectQuery(''))
.select({[_cn]: `${vtn}.${cn}`, ...this.dbModels[child].selectQuery(fields)}) // ...fields.split(',')); .select({[`${tn}_${vcn}`]: `${vtn}.${vcn}`, ...this.dbModels[child].selectQuery(fields)}) // ...fields.split(','));
this._paginateAndSort(query, {sort, limit, offset}, null, true); this._paginateAndSort(query, {sort, limit, offset}, null, true);
return this.isSqlite() ? this.dbDriver.select().from(query) : query; return this.isSqlite() ? this.dbDriver.select().from(query) : query;
}), !this.isSqlite() }), !this.isSqlite()
)); ));
let gs = _.groupBy(childs, _cn); let gs = _.groupBy(childs, `${tn}_${vcn}`);
parent.forEach(row => { parent.forEach(row => {
row[this.dbModels?.[child]?._tn || child] = gs[row[this.pks[0]._cn]] || []; row[this.dbModels?.[child]?._tn || child] = gs[row[this.pks[0]._cn]] || [];
}) })

3
packages/nocodb/src/lib/noco/NcProjectBuilder.ts

@ -91,6 +91,9 @@ export default class NcProjectBuilder {
type: 'AUTH_MIDDLEWARE' type: 'AUTH_MIDDLEWARE'
}); });
break; break;
case 'xcM2MRelationCreate':
curBuilder.onManyToManyRelationCreate(data.req.args.parentTable, data.req.args.childTable, data.req.args);
break;
case 'relationCreate': case 'relationCreate':
await curBuilder.onRelationCreate(data.req.args.parentTable, data.req.args.childTable, data.req.args); await curBuilder.onRelationCreate(data.req.args.parentTable, data.req.args.childTable, data.req.args);

131
packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts

@ -322,7 +322,14 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
const oldModelRow = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { const oldModelRow = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {
title: tn title: tn
}); })
let queryParams;
try {
queryParams = JSON.parse(oldModelRow.query_params);
} catch (e) {
}
if (!oldModelRow) { if (!oldModelRow) {
return; return;
@ -359,7 +366,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
this.baseLog(`onTableUpdate : Generating new model meta for '%s' table`, tn) this.baseLog(`onTableUpdate : Generating new model meta for '%s' table`, tn)
/* create models from table */ /* create models from table */
const newMeta = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); const newMeta:any = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject();
/* get ACL row */ /* get ACL row */
@ -367,6 +374,11 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
const acl = JSON.parse(aclRow.acl); const acl = JSON.parse(aclRow.acl);
const oldMeta = JSON.parse(oldModelRow.meta); const oldMeta = JSON.parse(oldModelRow.meta);
// copy virtual columns and many to many relations from old meta to new
newMeta.v = oldMeta.v;
newMeta.manyToMany = oldMeta.manyToMany;
const aclOper = []; const aclOper = [];
this.baseLog(`onTableUpdate : Comparing and updating new metadata of '%s' table`, tn) this.baseLog(`onTableUpdate : Comparing and updating new metadata of '%s' table`, tn)
@ -519,6 +531,12 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
aclObj.columns[column.cn] = true; aclObj.columns[column.cn] = true;
} }
} }
if (queryParams?.showFields) {
queryParams.showFields[column.cno] = true;
}
} else { } else {
oldCol = oldMeta.columns.find(c => c.cn === column.cn); oldCol = oldMeta.columns.find(c => c.cn === column.cn);
newCol = newMeta.columns.find(c => c.cn === column.cn); newCol = newMeta.columns.find(c => c.cn === column.cn);
@ -535,7 +553,8 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
} }
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
meta: JSON.stringify(newMeta) meta: JSON.stringify(newMeta),
...(queryParams ? {query_params: JSON.stringify(queryParams)} : {})
}, { }, {
title: tn title: tn
}); });
@ -727,6 +746,11 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
} }
public async onManyToManyRelationCreate(parent: string, child: string, _args?: any) {
return this.getManyToManyRelations({parent, child})
}
protected async loadCommon(): Promise<any> { protected async loadCommon(): Promise<any> {
this.baseLog(`loadCommon :`); this.baseLog(`loadCommon :`);
@ -1497,7 +1521,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
} }
protected async getManyToManyRelations() { protected async getManyToManyRelations({parent = null, child = null} = {}) {
const metas = new Set<any>(); const metas = new Set<any>();
const assocMetas = new Set<any>(); const assocMetas = new Set<any>();
@ -1506,6 +1530,11 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// check if table is a Bridge table(or Associative Table) by checking // check if table is a Bridge table(or Associative Table) by checking
// number of foreign keys and columns // number of foreign keys and columns
if (meta.belongsTo?.length === 2 && meta.columns.length < 5) { if (meta.belongsTo?.length === 2 && meta.columns.length < 5) {
if (parent && child && !([parent, child].includes(meta.belongsTo[0].rtn) && [parent, child].includes(meta.belongsTo[1].rtn))) {
continue;
}
const tableMetaA = this.metas[meta.belongsTo[0].rtn]; const tableMetaA = this.metas[meta.belongsTo[0].rtn];
const tableMetaB = this.metas[meta.belongsTo[1].rtn]; const tableMetaB = this.metas[meta.belongsTo[1].rtn];
@ -1515,35 +1544,39 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// add manytomany data under metadata of both related columns // add manytomany data under metadata of both related columns
tableMetaA.manyToMany = tableMetaA.manyToMany || []; tableMetaA.manyToMany = tableMetaA.manyToMany || [];
tableMetaA.manyToMany.push({ if (tableMetaA.manyToMany.every(mm => mm.vtn === meta.vtn)) {
"tn": tableMetaA.tn, tableMetaA.manyToMany.push({
"cn": meta.belongsTo[0].rcn, "tn": tableMetaA.tn,
"vtn": meta.tn, "cn": meta.belongsTo[0].rcn,
"vcn": meta.belongsTo[0].cn, "vtn": meta.tn,
"vrcn": meta.belongsTo[1].cn, "vcn": meta.belongsTo[0].cn,
"rtn": meta.belongsTo[1].rtn, "vrcn": meta.belongsTo[1].cn,
"rcn": meta.belongsTo[1].rcn, "rtn": meta.belongsTo[1].rtn,
"_tn": tableMetaA._tn, "rcn": meta.belongsTo[1].rcn,
"_cn": meta.belongsTo[0]._rcn, "_tn": tableMetaA._tn,
"_rtn": meta.belongsTo[1]._rtn, "_cn": meta.belongsTo[0]._rcn,
"_rcn": meta.belongsTo[1]._rcn "_rtn": meta.belongsTo[1]._rtn,
}) "_rcn": meta.belongsTo[1]._rcn
})
metas.add(tableMetaA)
}
tableMetaB.manyToMany = tableMetaB.manyToMany || []; tableMetaB.manyToMany = tableMetaB.manyToMany || [];
tableMetaB.manyToMany.push({ if (tableMetaB.manyToMany.every(mm => mm.vtn === meta.vtn)) {
"tn": tableMetaB.tn, tableMetaB.manyToMany.push({
"cn": meta.belongsTo[1].rcn, "tn": tableMetaB.tn,
"vtn": meta.tn, "cn": meta.belongsTo[1].rcn,
"vcn": meta.belongsTo[1].cn, "vtn": meta.tn,
"vrcn": meta.belongsTo[0].cn, "vcn": meta.belongsTo[1].cn,
"rtn": meta.belongsTo[0].rtn, "vrcn": meta.belongsTo[0].cn,
"rcn": meta.belongsTo[0].rcn, "rtn": meta.belongsTo[0].rtn,
"_tn": tableMetaB._tn, "rcn": meta.belongsTo[0].rcn,
"_cn": meta.belongsTo[1]._rcn, "_tn": tableMetaB._tn,
"_rtn": meta.belongsTo[0]._rtn, "_cn": meta.belongsTo[1]._rcn,
"_rcn": meta.belongsTo[0]._rcn "_rtn": meta.belongsTo[0]._rtn,
}) "_rcn": meta.belongsTo[0]._rcn
metas.add(tableMetaA) })
metas.add(tableMetaB) metas.add(tableMetaB)
}
assocMetas.add(meta) assocMetas.add(meta)
} }
} }
@ -1551,14 +1584,36 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// Update metadata of tables which have manytomany relation // Update metadata of tables which have manytomany relation
// and recreate basemodel with new meta information // and recreate basemodel with new meta information
for (const meta of metas) { for (const meta of metas) {
let queryParams;
// update showfields on new many to many relation create
if (parent && child) {
try {
queryParams = JSON.parse((await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {title: meta.tn})).query_params)
} catch (e) {
// ignore
}
}
meta.v = [ meta.v = [
...meta.v.filter(vc => vc.bt && meta.manyToMany.some(mm => vc.bt.rtn === mm.vtn)), ...meta.v.filter(vc => (vc.bt && meta.manyToMany.some(mm => vc.bt.rtn === mm.vtn)) || vc.mm),
...meta.manyToMany.map(mm => ({ // todo: ignore existing m2m relations
mm, ...meta.manyToMany.map(mm => {
_cn: `${mm._tn} <=> ${mm._rtn}`
}))] if (queryParams?.showFields && !(`${mm._tn} <=> ${mm._rtn}` in queryParams.showFields)) {
queryParams.showFields[`${mm._tn} <=> ${mm._rtn}`] = true;
}
return {
mm,
_cn: `${mm._tn} <=> ${mm._rtn}`
}
})]
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
meta: JSON.stringify(meta) meta: JSON.stringify(meta),
...(queryParams ? {query_params: JSON.stringify(queryParams)} : {})
}, {title: meta.tn}) }, {title: meta.tn})
XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::')); XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::'));
this.models[meta.tn] = this.getBaseModel(meta) this.models[meta.tn] = this.getBaseModel(meta)

173
packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts

@ -1466,6 +1466,10 @@ export default class NcMetaMgr {
result = await this.xcVirtualRelationCreate(args, req); result = await this.xcVirtualRelationCreate(args, req);
break; break;
case 'xcM2MRelationCreate':
result = await this.xcM2MRelationCreate(args, req);
break;
case 'xcVirtualRelationDelete': case 'xcVirtualRelationDelete':
result = await this.xcVirtualRelationDelete(args, req); result = await this.xcVirtualRelationDelete(args, req);
break; break;
@ -2277,6 +2281,175 @@ export default class NcMetaMgr {
return res; return res;
} }
protected async xcM2MRelationCreate(args: any, req): Promise<any> {
const dbAlias = this.getDbAlias(args);
const projectId = this.getProjectId(args);
try {
const parent = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
title: args.args.parentTable
});
const child = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
title: args.args.childTable
});
const parentMeta = JSON.parse(parent.meta);
const childMeta = JSON.parse(child.meta);
const parentPK = parentMeta.columns.find(c => c.pk);
const childPK = childMeta.columns.find(c => c.pk);
const associateTableCols = [];
associateTableCols.push({
cn: `${childMeta.tn}_id`,
_cn: `${childMeta.tn}_id`,
rqd: true,
pk: true,
ai: false,
cdf: null,
dt: childPK.dt,
dtxp: childPK.dtxp,
dtxs: childPK.dtxs,
un: childPK.un,
altered: 1
}, {
cn: `${parentMeta.tn}_id`,
_cn: `${parentMeta.tn}_id`,
rqd: true,
pk: true,
ai: false,
cdf: null,
dt: parentPK.dt,
dtxp: parentPK.dtxp,
dtxs: parentPK.dtxs,
un: parentPK.un,
altered: 1
});
const aTn = `nc_m2m_${parentMeta.tn}_${childMeta.tn}_${Math.floor(Math.random() * 1000)}`;
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableCreate', {
...args,
args: {
tn: aTn,
_tn: aTn,
columns: associateTableCols
}
});
if (this.listener) {
await this.listener({
req: {
...args,
args: {
tn: aTn,
_tn: aTn,
columns: associateTableCols
}, api: 'tableCreate'
},
res: out,
user: req.user,
ctx: {
req
}
});
}
const rel1Args = {
...args.args,
childTable: aTn,
childColumn: `${parentMeta.tn}_id`,
parentTable: parentMeta.tn,
parentColumn: parentPK.cn,
type: 'real'
};
const rel2Args = {
...args.args,
childTable: aTn,
childColumn: `${childMeta.tn}_id`,
parentTable: childMeta.tn,
parentColumn: childPK.cn,
type: 'real'
};
if (args.args.type === 'real') {
const outrel = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('relationCreate', {
...args,
args: rel1Args
});
if (this.listener) {
await this.listener({
req: {
...args,
args: rel1Args,
api: 'relationCreate'
},
res: outrel,
user: req.user,
ctx: {
req
}
});
}
const outrel1 = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('relationCreate', {
...args,
args: rel2Args
});
if (this.listener) {
await this.listener({
req: {
...args,
args: rel2Args,
api: 'relationCreate'
},
res: outrel1,
user: req.user,
ctx: {
req
}
});
}
} else {
const outrel = await this.xcVirtualRelationCreate({...args, args: rel1Args}, req);
if (this.listener) {
await this.listener({
req: {
...args,
args: rel1Args,
api: 'xcVirtualRelationCreate'
},
res: outrel,
user: req.user,
ctx: {
req
}
});
}
const outrel1 = await this.xcVirtualRelationCreate({...args, args: rel2Args}, req);
await this.listener({
req: {
...args,
args: rel2Args,
api: 'xcVirtualRelationCreate'
},
res: outrel1,
user: req.user,
ctx: {
req
}
});
}
} catch (e) {
console.log(e.message)
}
}
protected async xcVirtualRelationDelete(args: any, req): Promise<any> { protected async xcVirtualRelationDelete(args: any, req): Promise<any> {
const dbAlias = this.getDbAlias(args); const dbAlias = this.getDbAlias(args);
const projectId = this.getProjectId(args); const projectId = this.getProjectId(args);

33
packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts

@ -1007,6 +1007,13 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
// update old model meta with new details // update old model meta with new details
const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp}); const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp});
let queryParams;
try {
queryParams = JSON.parse(existingModel.query_params);
} catch (e) { /* */
}
swaggerArr.push(JSON.parse(existingModel.schema)); swaggerArr.push(JSON.parse(existingModel.schema));
if (existingModel) { if (existingModel) {
this.log(`onRelationCreate : Updating model metadata for parent table '%s'`, tnp); this.log(`onRelationCreate : Updating model metadata for parent table '%s'`, tnp);
@ -1020,6 +1027,8 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
hasMany: meta.hasMany, hasMany: meta.hasMany,
}); });
/* Add new has many relation to virtual columns */ /* Add new has many relation to virtual columns */
oldMeta.v = oldMeta.v || []; oldMeta.v = oldMeta.v || [];
oldMeta.v.push({ oldMeta.v.push({
@ -1027,9 +1036,14 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
_cn: `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}` _cn: `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}`
}) })
if (queryParams?.showFields) {
queryParams.showFields[`${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}`] = true;
}
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
title: tnp,
meta: JSON.stringify(oldMeta), meta: JSON.stringify(oldMeta),
...(queryParams ? {query_params: JSON.stringify(queryParams)} : {})
}, {'title': tnp}) }, {'title': tnp})
} }
@ -1089,6 +1103,14 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
// update old model meta with new details // update old model meta with new details
const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnc}); const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnc});
let queryParams;
try {
queryParams = JSON.parse(existingModel.query_params);
} catch (e) { /* */
}
swaggerArr.push(JSON.parse(existingModel.schema)) swaggerArr.push(JSON.parse(existingModel.schema))
if (existingModel) { if (existingModel) {
meta.belongsTo.forEach(hm => { meta.belongsTo.forEach(hm => {
@ -1108,9 +1130,14 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
_cn: `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}` _cn: `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}`
}) })
if (queryParams?.showFields) {
queryParams.showFields[`${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}`] = true;
}
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
title: tnc, meta: JSON.stringify(oldMeta),
meta: JSON.stringify(oldMeta) ...(queryParams ? {query_params: JSON.stringify(queryParams)} : {})
}, {'title': tnc}) }, {'title': tnc})
} }

86
packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts

@ -0,0 +1,86 @@
import BaseRender from "../../BaseRender";
abstract class BaseModelXcMeta extends BaseRender {
public abstract getXcColumnsObject(context:any):any[];
public getObject() {
return {
tn: this.ctx.tn,
_tn: this.ctx._tn,
columns: this.getXcColumnsObject(this.ctx),
pks: [],
hasMany: this.ctx.hasMany,
belongsTo: this.ctx.belongsTo,
db_type: this.ctx.db_type,
type: this.ctx.type,
v: [
...(this.ctx.hasMany || []).map(hm => ({
hm,
_cn:`${hm._rtn} => ${hm._tn}`
})),
...(this.ctx.belongsTo || []).map(bt => ({
bt,
_cn:`${bt._rtn} <= ${bt._tn}`
})),
]
}
}
protected mapDefaultPrimaryValue(columnsArr: any[]):void {
// pk can be at the end
//
/*
if PK is at the end of table
if (there is a column for PV)
make that PV
else
lets think
else if (pk is not at the end of table)
if (there is a column for PV)
make that PV
else
lets think
else if ( no pk at all)
let's think
*/
if (!columnsArr.some(column => column.pv)) {
let len = columnsArr.length;
let pkIndex = -1;
while (len--) {
if (columnsArr[len].pk) {
pkIndex = len;
break;
}
}
// if PK is at the end of table
if (pkIndex === columnsArr.length - 1) {
if (pkIndex > 0) {
columnsArr[pkIndex - 1].pv = true;
}
}
// pk is not at the end of table
else if (pkIndex > -1) {
columnsArr[pkIndex + 1].pv = true;
}
// no pk at all
else {
// todo:
}
}
}
}
export default BaseModelXcMeta;

459
packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXc.ts

@ -1,459 +0,0 @@
import BaseRender from "../../BaseRender";
class MssqlXcRender extends BaseRender {
/**
*
* @param dir
* @param filename
* @param ctx
* @param ctx.tn
* @param ctx.columns
* @param ctx.relations
*/
constructor({dir, filename, ctx}) {
super({dir, filename, ctx});
}
/**
* Prepare variables used in code template
*/
prepare() {
const data:any = {};
/* example of simple variable */
data.tn = this.ctx.tn;
/* for complex code provide a func and args - do derivation within the func cbk */
data.columns = {
func: this._renderXcColumns.bind(this),
args: {columns: this.ctx.columns, relations: this.ctx.relations, tn: this.ctx.tn}
};
data.pks = {
func: this._renderXcPks.bind(this),
args: {columns: this.ctx.columns, relations: this.ctx.relations, tn: this.ctx.tn}
};
/* for complex code provide a func and args - do derivation within the func cbk */
data.relations = {
func: this._renderXcRelations.bind(this),
args: {columns: this.ctx.columns, relations: this.ctx.relations, tn: this.ctx.tn}
};
return data;
}
/**
*
* @param args
* @param args.columns
* @param args.relations
* @returns {string}
* @private
*/
_renderXcColumns(args) {
let str = '[\r\n';
for (let i = 0; i < args.columns.length; ++i) {
str += `{\r\n`;
str += `cn: '${args.columns[i].cn}',\r\n`;
str += `dt: '${args.columns[i].dt}',\r\n`;
if (args.columns[i].rqd)
str += `rqd: ${args.columns[i].rqd},\r\n`;
if (args.columns[i].cdf)
str += `default: "${args.columns[i].cdf}",\r\n`;
if (args.columns[i].un)
str += `un: ${args.columns[i].un},\r\n`;
if (args.columns[i].pk)
str += `pk: ${args.columns[i].pk},\r\n`;
if (args.columns[i].ai)
str += `ai: ${args.columns[i].ai},\r\n`;
str += `validate: {
func: [],
args: [],
msg: []
},`;
str += `},\r\n`;
}
str += ']\r\n';
return str;
}
/**
*
* @param args
* @param args.columns
* @param args.relations
* @returns {string}
* @private
*/
_renderXcPks(args) {
let str = '{\r\n';
for (let i = 0; i < args.columns.length; ++i) {
str += `${args.columns[i].cn} : {\r\n`;
if (args.columns[i].rqd)
str += `rqd: ${args.columns[i].rqd},`;
if (args.columns[i].cdf)
str += `default: "${args.columns[i].cdf}",`;
if (args.columns[i].un)
str += `un: ${args.columns[i].un},`;
if (args.columns[i].pk)
str += `pk: ${args.columns[i].pk},`;
if (args.columns[i].ai)
str += `ai: ${args.columns[i].ai},`;
str += `validate: {
func: [],
args: [],
msg: []
},`;
str += `},\r\n`;
}
str += '}\r\n';
return str;
}
/**
*
* @param args
* @param args.tn
* @param args.columns
* @param args.relations
* @returns {string}
* @private
*/
_renderXcRelations(args) {
let relationStr = '';
let belongsToRelations = args.relations.filter(r => r.tn === args.tn);
let hasManyRelations = args.relations.filter(r => r.rtn === args.tn);
if (belongsToRelations.length || hasManyRelations.length) {
relationStr = `${args.tn}.associate = function(models) {`
for (let i = 0; i < belongsToRelations.length; ++i) {
relationStr += `${args.tn}.belongsTo(models.${belongsToRelations[i].rtn});\n`
}
for (let i = 0; i < hasManyRelations.length; ++i) {
relationStr += `${args.tn}.hasMany(models.${hasManyRelations[i]._tn});\n`
}
relationStr += `}`
}
return relationStr;
}
_sequelizeGetType(column) {
let str = '';
switch (column.dt) {
case "int":
str += `DataTypes.INTEGER(${column.dtxp})`;
if (column.un)
str += `.UNSIGNED`;
break;
case "tinyint":
str += `DataTypes.INTEGER(${column.dtxp})`;
if (column.un)
str += `.UNSIGNED`;
break;
case "smallint":
str += `DataTypes.INTEGER(${column.dtxp})`;
if (column.un)
str += `.UNSIGNED`;
break;
case "mediumint":
str += `DataTypes.INTEGER(${column.dtxp})`;
if (column.un)
str += `.UNSIGNED`;
break;
case "bigint":
str += `DataTypes.BIGINT`;
if (column.un)
str += `.UNSIGNED`;
break;
case "float":
str += `DataTypes.FLOAT`;
break;
case "decimal":
str += `DataTypes.DECIMAL`;
break;
case "double":
str += `"DOUBLE(${column.dtxp},${column.ns})"`;
break;
case "real":
str += `DataTypes.FLOAT`;
break;
case "bit":
str += `DataTypes.BOOLEAN`;
break;
case "boolean":
str += `DataTypes.STRING(45)`;
break;
case "serial":
str += `DataTypes.BIGINT`;
break;
case "date":
str += `DataTypes.DATEONLY`;
break;
case "datetime":
str += `DataTypes.DATE`;
break;
case "timestamp":
str += `DataTypes.DATE`;
break;
case "time":
str += `DataTypes.TIME`;
break;
case "year":
str += `"YEAR"`;
break;
case "char":
str += `DataTypes.CHAR(${column.dtxp})`;
break;
case "varchar":
str += `DataTypes.STRING(${column.dtxp})`;
break;
case "nchar":
str += `DataTypes.CHAR(${column.dtxp})`;
break;
case "text":
str += `DataTypes.TEXT`;
break;
case "tinytext":
str += `DataTypes.TEXT`;
break;
case "mediumtext":
str += `DataTypes.TEXT`;
break;
case "longtext":
str += `DataTypes.TEXT`;
break;
case "binary":
str += `"BINARY(${column.dtxp})"`;
break;
case "varbinary":
str += `"VARBINARY(${column.dtxp})"`;
break;
case "blob":
str += `"BLOB"`;
break;
case "tinyblob":
str += `"TINYBLOB"`;
break;
case "mediumblob":
str += `"MEDIUMBLOB"`;
break;
case "longblob":
str += `"LONGBLOB"`;
break;
case "enum":
str += `DataTypes.ENUM(${column.dtxp})`;
break;
case "set":
str += `"SET(${column.dtxp})"`;
break;
case "time":
str += `DataTypes.TIME`;
break;
case "geometry":
str += `DataTypes.GEOMETRY`;
break;
case "point":
str += `"POINT"`;
break;
case "linestring":
str += `"LINESTRING"`;
break;
case "polygon":
str += `"POLYGON"`;
break;
case "multipoint":
str += `"MULTIPOINT"`;
break;
case "multilinestring":
str += `"MULTILINESTRING"`;
break;
case "multipolygon":
str += `"MULTIPOLYGON"`;
break;
case "json":
str += `DataTypes.JSON`;
break;
default:
str += `"${column.dt}"`;
break;
}
return str;
}
_sequelizeGetDefault(column) {
let str = '';
switch (column.dt) {
case "int":
str += `'${column.cdf}'`;
break;
case "tinyint":
str += `'${column.cdf}'`;
break;
case "smallint":
str += `'${column.cdf}'`;
break;
case "mediumint":
str += `'${column.cdf}'`;
break;
case "bigint":
str += `'${column.cdf}'`;
break;
case "float":
str += `'${column.cdf}'`;
break;
case "decimal":
str += `'${column.cdf}'`;
break;
case "double":
str += `'${column.cdf}'`;
break;
case "real":
str += `'${column.cdf}'`;
break;
case "bit":
str += column.cdf ? column.cdf.split('b')[1] : column.cdf;
break;
case "boolean":
str += column.cdf;
break;
case "serial":
str += column.cdf;
break;
case "date":
str += `sequelize.literal('${column.cdf_sequelize}')`;
break;
case "datetime":
str += `sequelize.literal('${column.cdf_sequelize}')`;
break;
case "timestamp":
str += `sequelize.literal('${column.cdf_sequelize}')`;
break;
case "time":
str += `'${column.cdf}'`;
break;
case "year":
str += `'${column.cdf}'`;
break;
case "char":
str += `'${column.cdf}'`;
break;
case "varchar":
str += `'${column.cdf}'`;
break;
case "nchar":
str += `'${column.cdf}'`;
break;
case "text":
str += column.cdf;
break;
case "tinytext":
str += column.cdf;
break;
case "mediumtext":
str += column.cdf;
break;
case "longtext":
str += column.cdf;
break;
case "binary":
str += column.cdf;
break;
case "varbinary":
str += column.cdf;
break;
case "blob":
str += column.cdf;
break;
case "tinyblob":
str += column.cdf;
break;
case "mediumblob":
str += column.cdf;
break;
case "longblob":
str += column.cdf;
break;
case "enum":
str += `'${column.cdf}'`;
break;
case "set":
str += `'${column.cdf}'`;
break;
case "geometry":
str += `'${column.cdf}'`;
break;
case "point":
str += `'${column.cdf}'`;
break;
case "linestring":
str += `'${column.cdf}'`;
break;
case "polygon":
str += `'${column.cdf}'`;
break;
case "multipoint":
str += `'${column.cdf}'`;
break;
case "multilinestring":
str += `'${column.cdf}'`;
break;
case "multipolygon":
str += `'${column.cdf}'`;
break;
case "json":
str += column.cdf;
break;
}
return str;
}
}
export default MssqlXcRender;

9
packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMssql.ts

@ -1,6 +1,6 @@
import BaseRender from "../../BaseRender"; import BaseModelXcMeta from "./BaseModelXcMeta";
class ModelXcMetaMssql extends BaseRender { class ModelXcMetaMssql extends BaseModelXcMeta{
/** /**
* @param dir * @param dir
@ -570,9 +570,13 @@ class ModelXcMetaMssql extends BaseRender {
columnsArr.push(columnObj) columnsArr.push(columnObj)
} }
this.mapDefaultPrimaryValue(columnsArr);
return columnsArr; return columnsArr;
} }
/*
getObject() { getObject() {
return { return {
tn: this.ctx.tn, tn: this.ctx.tn,
@ -586,6 +590,7 @@ class ModelXcMetaMssql extends BaseRender {
} }
} }
*/
_getUIDataType(col):any { _getUIDataType(col):any {

77
packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMysql.ts

@ -1,6 +1,6 @@
import BaseRender from "../../BaseRender"; import BaseModelXcMeta from "./BaseModelXcMeta";
class ModelXcMetaMysql extends BaseRender { class ModelXcMetaMysql extends BaseModelXcMeta {
/** /**
* @param dir * @param dir
@ -180,59 +180,11 @@ class ModelXcMetaMysql extends BaseRender {
columnsArr.push(columnObj) columnsArr.push(columnObj)
} }
this.mapDefaultPrimaryValue(columnsArr);
// pk can be at the end
//
/*
if PK is at the end of table
if (there is a column for PV)
make that PV
else
lets think
else if (pk is not at the end of table)
if (there is a column for PV)
make that PV
else
lets think
else if ( no pk at all)
let's think
*/
if (!columnsArr.some(column => column.pv)) {
let len = columnsArr.length;
let pkIndex = -1;
while (len--) {
if (columnsArr[len].pk) {
pkIndex = len;
break;
}
}
// if PK is at the end of table
if (pkIndex === columnsArr.length - 1) {
if (pkIndex > 0) {
columnsArr[pkIndex - 1].pv = true;
}
}
// pk is not at the end of table
else if (pkIndex > -1) {
columnsArr[pkIndex + 1].pv = true;
}
// no pk at all
else {
// todo:
}
}
return columnsArr; return columnsArr;
} }
_getAbstractType(column) { _getAbstractType(column) {
let str = ''; let str = '';
@ -605,30 +557,7 @@ class ModelXcMetaMysql extends BaseRender {
} }
getObject() {
return {
tn: this.ctx.tn,
_tn: this.ctx._tn,
columns: this.getXcColumnsObject(this.ctx),
pks: [],
hasMany: this.ctx.hasMany,
belongsTo: this.ctx.belongsTo,
db_type: this.ctx.db_type,
type: this.ctx.type,
v: [
...(this.ctx.hasMany || []).map(hm => ({
hm,
_cn:`${hm._rtn} => ${hm._tn}`
})),
...(this.ctx.belongsTo || []).map(bt => ({
bt,
_cn:`${bt._rtn} <= ${bt._tn}`
})),
]
}
}
} }

9
packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaOracle.ts

@ -1,6 +1,6 @@
import BaseRender from "../../BaseRender"; import BaseModelXcMeta from "./BaseModelXcMeta";
class ModelXcMetaOracle extends BaseRender { class ModelXcMetaOracle extends BaseModelXcMeta {
/** /**
* @param dir * @param dir
@ -182,6 +182,7 @@ class ModelXcMetaOracle extends BaseRender {
} }
this.mapDefaultPrimaryValue(columnsArr);
return columnsArr; return columnsArr;
} }
@ -527,7 +528,7 @@ class ModelXcMetaOracle extends BaseRender {
return str; return str;
} }
/*
getObject() { getObject() {
return { return {
tn: this.ctx.tn, tn: this.ctx.tn,
@ -540,7 +541,7 @@ class ModelXcMetaOracle extends BaseRender {
type: this.ctx.type, type: this.ctx.type,
} }
} }*/
} }

10
packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaPg.ts

@ -1,6 +1,6 @@
import BaseRender from "../../BaseRender"; import BaseModelXcMeta from "./BaseModelXcMeta";
class ModelXcMetaPg extends BaseRender { class ModelXcMetaPg extends BaseModelXcMeta {
/** /**
* @param dir * @param dir
@ -984,10 +984,12 @@ class ModelXcMetaPg extends BaseRender {
columnsArr.push(columnObj) columnsArr.push(columnObj)
} }
this.mapDefaultPrimaryValue(columnsArr);
return columnsArr; return columnsArr;
} }
getObject() { /* getObject() {
return { return {
tn: this.ctx.tn, tn: this.ctx.tn,
_tn: this.ctx._tn, _tn: this.ctx._tn,
@ -999,7 +1001,7 @@ class ModelXcMetaPg extends BaseRender {
type: this.ctx.type, type: this.ctx.type,
} }
} }*/
} }

9
packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaSqlite.ts

@ -1,6 +1,6 @@
import BaseRender from "../../BaseRender"; import BaseModelXcMeta from "./BaseModelXcMeta";
class ModelXcMetaSqlite extends BaseRender { class ModelXcMetaSqlite extends BaseModelXcMeta {
/** /**
* @param dir * @param dir
@ -530,10 +530,11 @@ class ModelXcMetaSqlite extends BaseRender {
columnsArr.push(columnObj) columnsArr.push(columnObj)
} }
this.mapDefaultPrimaryValue(columnsArr);
return columnsArr; return columnsArr;
} }
public getObject(): any { /* public getObject(): any {
return { return {
tn: this.ctx.tn, tn: this.ctx.tn,
_tn: this.ctx._tn, _tn: this.ctx._tn,
@ -545,7 +546,7 @@ class ModelXcMetaSqlite extends BaseRender {
type: this.ctx.type, type: this.ctx.type,
} }
} }*/
private _getUIDataType(col): any { private _getUIDataType(col): any {
switch (this.getAbstractType(col)) { switch (this.getAbstractType(col)) {

Loading…
Cancel
Save