console/mssql: support CRUD operations for foreign keys

adds foreign key functionality to mssql tables under Modify Tab.

Steps to test:
1. Connect an MSSQL data source.
2. Create a table
3. go to modify page
4. make sure the `Add foreign key `button is visible
5. click on `Add foreign key`, and try to add FK
6. after adding the FK, we will see the `edit` and `remove` button, try to edit and remove the FK

Co-authored-by: Vijay Prasanna <11921040+vijayprasanna13@users.noreply.github.com>
GitOrigin-RevId: 63be7c4ef909503ac05096a64c686b72f7c47307
This commit is contained in:
Varun Choudhary 2021-06-03 17:18:25 +05:30 committed by hasura-bot
parent 205a31de0c
commit f3643f26ad
10 changed files with 257 additions and 20 deletions

View File

@ -4,6 +4,7 @@
### Bug fixes and improvements
(Add entries below in the order of server, console, cli, docs, others)
- console: add foreign key CRUD functionality to ms sql server tables
## v2.0.0-beta.1

View File

@ -1,3 +1,5 @@
import { dataSource } from '../../../../dataSources';
const defaultState = {
tableName: null,
tableComment: null,
@ -13,8 +15,8 @@ const defaultState = {
refColumn: '',
},
],
onUpdate: 'restrict',
onDelete: 'restrict',
onUpdate: dataSource?.violationActions?.[0],
onDelete: dataSource?.violationActions?.[0],
},
],
uniqueKeys: [[]],

View File

@ -74,6 +74,7 @@ const ForeignKeySelector = ({
// dispatch action for setting reference table
const dispatchSetRefTable = event => {
const newFks = JSON.parse(JSON.stringify(foreignKeys));
const violiationActions = dataSource?.violationActions;
if (newFks[index].refTableName !== event.target.value) {
newFks[index].colMappings = [{ column: '', refColumn: '' }];
}
@ -88,8 +89,8 @@ const ForeignKeySelector = ({
refColumn: '',
},
],
onUpdate: 'restrict',
onDelete: 'restrict',
onUpdate: violiationActions?.[0],
onDelete: violiationActions?.[0],
});
}
dispatch(setForeignKeys(newFks));

View File

@ -1,3 +1,5 @@
import { dataSource } from '../../../dataSources';
const defaultCurFilter = {
where: { $and: [{ '': { $eq: '' } }] },
limit: 10,
@ -89,8 +91,8 @@ const defaultModifyState = {
refSchemaName: '',
refTableName: '',
colMappings: [{ '': '' }],
onDelete: 'restrict',
onUpdate: 'restrict',
onDelete: dataSource?.violationActions?.[0],
onUpdate: dataSource?.violationActions?.[0],
},
],
checkConstraintsModify: [],

View File

@ -14,6 +14,7 @@ import ForeignKeySelector from '../Common/Components/ForeignKeySelector';
import { updateSchemaInfo } from '../DataActions';
import { getConfirmation } from '../../../Common/utils/jsUtils';
import { dataSource } from '../../../../dataSources';
const ForeignKeyEditor = ({
tableSchema,
@ -43,8 +44,8 @@ const ForeignKeyEditor = ({
existingForeignKeys.push({
refSchemaName: '',
refTableName: '',
onUpdate: 'restrict',
onDelete: 'restrict',
onUpdate: dataSource?.violationActions?.[0],
onDelete: dataSource?.violationActions?.[0],
colMappings: [{ column: '', refColumn: '' }],
});
useEffect(() => {

View File

@ -655,7 +655,7 @@ const saveForeignKeys = (index, tableSchema, columns) => {
const requestMsg = 'Saving foreign key...';
const successMsg = 'Foreign key saved';
const errorMsg = 'Failed setting foreign key';
const violiationActions = dataSource?.violationActions;
const customOnSuccess = () => {
if (!constraintName) {
const newFks = [...getState().tables.modify.fkModify];
@ -666,8 +666,8 @@ const saveForeignKeys = (index, tableSchema, columns) => {
{
refTableName: '',
colMappings: [{ column: '', refColumn: '' }],
onUpdate: 'restrict',
onDelete: 'restrict',
onUpdate: violiationActions?.[0],
onDelete: violiationActions?.[0],
},
])
);

View File

@ -72,6 +72,21 @@ type MSSqlCheckConstraint = {
check_definition: string;
};
const modifyViolationType = (fkType: string) => {
switch (fkType) {
case 'NO_ACTION':
return 'no action';
case 'CASCADE':
return 'cascade';
case 'SET_NULL':
return 'set null';
case 'SET_DEFAULT':
return 'set default';
default:
return fkType;
}
};
export const mergeDataMssql = (
data: Array<{ result: string[] }>,
metadataTables: TableEntry[]
@ -183,6 +198,8 @@ export const mergeDataMssql = (
...fk,
column_mapping: mapping,
ref_table_table_schema: fk.ref_table_schema,
on_delete: modifyViolationType(fk.on_delete),
on_update: modifyViolationType(fk.on_update),
};
});

View File

@ -0,0 +1,49 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`mssql datasource tests getAlterFKSql should generate SQL query for altering foreign keys with multiple columns 1`] = `
"
BEGIN transaction;
ALTER TABLE \\"dbo\\".\\"user\\"
DROP CONSTRAINT IF EXISTS \\"oldConstraint\\";
ALTER TABLE \\"dbo\\".\\"user\\"
ADD CONSTRAINT \\"newConstraint\\"
FOREIGN KEY (id, id2)
REFERENCES \\"dbo\\".\\"user1\\" (id, id2)
ON UPDATE cascade ON DELETE cascade;
COMMIT transaction;
"
`;
exports[`mssql datasource tests getAlterFKSql should generate SQL query for altering foreign keys with one column 1`] = `
"
BEGIN transaction;
ALTER TABLE \\"dbo\\".\\"user\\"
DROP CONSTRAINT IF EXISTS \\"oldConstraint\\";
ALTER TABLE \\"dbo\\".\\"user\\"
ADD CONSTRAINT \\"newConstraint\\"
FOREIGN KEY (id)
REFERENCES \\"dbo\\".\\"user1\\" (id)
ON UPDATE no action ON DELETE cascade;
COMMIT transaction;
"
`;
exports[`mssql datasource tests getCreateFKeySql should generate query for create foreign keys with multiple columns 1`] = `
"
ALTER TABLE \\"dbo\\".\\"user\\"
ADD CONSTRAINT \\"newConstraint\\"
FOREIGN KEY (id, id2)
REFERENCES \\"dbo\\".\\"user1\\" (id, id2)
ON UPDATE cascade ON DELETE cascade"
`;
exports[`mssql datasource tests getCreateFKeySql should generate query for create foreign keys with one columns 1`] = `
"
ALTER TABLE \\"dbo\\".\\"user\\"
ADD CONSTRAINT \\"newConstraint\\"
FOREIGN KEY (id)
REFERENCES \\"dbo\\".\\"user1\\" (id)
ON UPDATE cascade ON DELETE cascade"
`;
exports[`mssql datasource tests getDropConstraintSql should generate query for droping foreign keys 1`] = `"ALTER TABLE \\"schemaName\\".\\"tableName1\\" DROP CONSTRAINT \\"constraintName\\""`;

View File

@ -5,6 +5,11 @@ describe('mssql datasource tests', () => {
it('should have delete enabled', () => {
expect(mssql?.supportedFeatures?.tables?.modify?.delete).toBe(true);
});
it('should have edit FK enabled', () => {
expect(mssql?.supportedFeatures?.tables?.modify?.foreignKeys?.edit).toBe(
true
);
});
});
describe('getDropSql', () => {
const { getDropSql } = mssql;
@ -21,4 +26,115 @@ describe('mssql datasource tests', () => {
expect(query2).toContain(`DROP table "public"."users";`);
});
});
describe('getAlterFKSql', () => {
const { getAlterForeignKeySql } = mssql;
it('should generate SQL query for altering foreign keys with one column', () => {
const query = getAlterForeignKeySql(
{
tableName: 'user',
schemaName: 'dbo',
columns: ['id'],
},
{
tableName: 'user1',
schemaName: 'dbo',
columns: ['id'],
},
'oldConstraint',
'newConstraint',
'no action',
'cascade'
);
expect(query).toContain(`DROP CONSTRAINT IF EXISTS "oldConstraint"`);
expect(query).toContain(`ADD CONSTRAINT "newConstraint"`);
expect(query).toContain(` ON DELETE cascade`);
expect(query).toContain(` ON UPDATE no action`);
expect(query).toMatchSnapshot();
});
it('should generate SQL query for altering foreign keys with multiple columns', () => {
const query = getAlterForeignKeySql(
{
tableName: 'user',
schemaName: 'dbo',
columns: ['id', 'id2'],
},
{
tableName: 'user1',
schemaName: 'dbo',
columns: ['id', 'id2'],
},
'oldConstraint',
'newConstraint',
'cascade',
'cascade'
);
expect(query).toContain(`DROP CONSTRAINT IF EXISTS "oldConstraint"`);
expect(query).toContain(`ADD CONSTRAINT "newConstraint"`);
expect(query).toContain('FOREIGN KEY (id, id2)');
expect(query).toContain('REFERENCES "dbo"."user1" (id, id2)');
expect(query).toContain('ON UPDATE cascade ON DELETE cascade');
expect(query).toMatchSnapshot();
});
});
describe('getCreateFKeySql', () => {
const { getCreateFKeySql } = mssql;
it('should generate query for create foreign keys with multiple columns', () => {
const query = getCreateFKeySql(
{
tableName: 'user',
schemaName: 'dbo',
columns: ['id', 'id2'],
},
{
tableName: 'user1',
schemaName: 'dbo',
columns: ['id', 'id2'],
},
'newConstraint',
'cascade',
'cascade'
);
expect(query).toContain('ALTER TABLE "dbo"."user"');
expect(query).toContain('ADD CONSTRAINT "newConstraint"');
expect(query).toContain('FOREIGN KEY (id, id2)');
expect(query).toContain('REFERENCES "dbo"."user1" (id, id2)');
expect(query).toContain('ON UPDATE cascade ON DELETE cascade');
expect(query).toMatchSnapshot();
});
it('should generate query for create foreign keys with one columns', () => {
const query = getCreateFKeySql(
{
tableName: 'user',
schemaName: 'dbo',
columns: ['id'],
},
{
tableName: 'user1',
schemaName: 'dbo',
columns: ['id'],
},
'newConstraint',
'cascade',
'cascade'
);
expect(query).toContain('ALTER TABLE "dbo"."user"');
expect(query).toContain('ADD CONSTRAINT "newConstraint"');
expect(query).toMatchSnapshot();
});
});
describe('getDropConstraintSql', () => {
const { getDropConstraintSql } = mssql;
it('should generate query for droping foreign keys', () => {
const query = getDropConstraintSql(
'tableName1',
'schemaName',
'constraintName'
);
expect(query).toContain(`DROP CONSTRAINT "constraintName"`);
expect(query).toMatchSnapshot();
});
});
});

View File

@ -130,7 +130,7 @@ export const supportedFeatures: SupportedFeaturesType = {
},
foreignKeys: {
view: true,
edit: false,
edit: true,
},
uniqueKeys: {
view: true,
@ -480,14 +480,62 @@ FROM sys.objects as obj
isTimeoutError: () => {
return false;
},
getAlterForeignKeySql: () => {
return '';
getAlterForeignKeySql: (
from: {
tableName: string;
schemaName: string;
columns: string[];
},
to: {
tableName: string;
schemaName: string;
columns: string[];
},
dropConstraint: string,
newConstraint: string,
onUpdate: string,
onDelete: string
) => {
return `
BEGIN transaction;
ALTER TABLE "${from.schemaName}"."${from.tableName}"
DROP CONSTRAINT IF EXISTS "${dropConstraint}";
ALTER TABLE "${from.schemaName}"."${from.tableName}"
ADD CONSTRAINT "${newConstraint}"
FOREIGN KEY (${from.columns.join(', ')})
REFERENCES "${to.schemaName}"."${to.tableName}" (${to.columns.join(', ')})
ON UPDATE ${onUpdate} ON DELETE ${onDelete};
COMMIT transaction;
`;
},
getCreateFKeySql: () => {
return '';
getCreateFKeySql: (
from: {
tableName: string;
schemaName: string;
columns: string[];
},
to: {
tableName: string;
schemaName: string;
columns: string[];
},
newConstraint: string,
onUpdate: string,
onDelete: string
) => {
return `
ALTER TABLE "${from.schemaName}"."${from.tableName}"
ADD CONSTRAINT "${newConstraint}"
FOREIGN KEY (${from.columns.join(', ')})
REFERENCES "${to.schemaName}"."${to.tableName}" (${to.columns.join(', ')})
ON UPDATE ${onUpdate} ON DELETE ${onDelete}`;
},
getDropConstraintSql: () => {
return '';
getDropConstraintSql: (
tableName: string,
schemaName: string,
constraintName: string
) => {
return `ALTER TABLE "${schemaName}"."${tableName}" DROP CONSTRAINT "${constraintName}"`;
},
getRenameTableSql: () => {
return '';
@ -679,8 +727,8 @@ INNER JOIN sys.schemas sch2
ON tab2.schema_id = sch2.schema_id for json path;
`;
},
getReferenceOption: () => {
return '';
getReferenceOption: (opt: string) => {
return opt;
},
deleteFunctionSql: () => {
return '';