Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/hck 7186 fe add support for snowflake tags for tables col #120

5 changes: 3 additions & 2 deletions forward_engineering/configs/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ module.exports = {
'\t\t${column_definitions}${out_of_line_constraints}\n' +
'\t)${tableOptions};\n',
columnDefinition:
'${name} ${type}${collation}${default}${identity}${autoincrement}${not_nul}${inline_constraint}${comment}',
'${name} ${type}${collation}${default}${identity}${autoincrement}${not_nul}${inline_constraint}${comment}${tag}',
externalColumnDefinition: '${name} ${type} as ${expression}${comment}',
createTableForeignKey: '${constraint}FOREIGN KEY (${columns}) REFERENCES ${primary_table} (${primary_columns})',
alterTableForeignKey:
'ALTER TABLE IF EXISTS ${table_name} ADD ${constraint}FOREIGN KEY (${columns}) REFERENCES ${primary_table} (${primary_columns});',
createView:
'CREATE${secure}${materialized} VIEW IF NOT EXISTS ${name} (\n' +
'\t${column_list}\n' +
')\n${copy_grants}${comment}AS ${select_statement};\n',
')\n${copy_grants}${comment}${tag}AS ${select_statement};\n',
createUDF:
'CREATE${orReplace} FUNCTION ${name}(${arguments})\n\tRETURNS ${returnType}${notNull}\n\tLANGUAGE ${language}${parameters}${comment}\n\tAS ${body};\n',
createProcedure:
Expand All @@ -48,4 +48,5 @@ module.exports = {
setPropertyTable: 'SET ${property};\n',
alterViewScript: 'ALTER VIEW IF EXISTS ${name} ',
alterMaterializedViewScript: 'ALTER MATERIALIZED VIEW ${name} ',
createTag: 'CREATE${orReplace} TAG${ifNotExist} ${name}${allowedValues}${comment};\n',
};
87 changes: 79 additions & 8 deletions forward_engineering/ddlProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ module.exports = (baseProvider, options, app) => {
tab,
});

const { getTagStatement, getTagAllowedValues, getTagKeyValues } = require('./helpers/tagHelper')({
getName,
toString,
});

const getOutOfLineConstraints = (
foreignKeyConstraints,
primaryKeyConstraints,
Expand Down Expand Up @@ -95,6 +100,8 @@ module.exports = (baseProvider, options, app) => {
fileFormats,
stages,
isCaseSensitive,
tags,
schemaTags,
}) {
const transientStatement = transient ? ' TRANSIENT' : '';
const dataRetentionStatement =
Expand Down Expand Up @@ -137,6 +144,7 @@ module.exports = (baseProvider, options, app) => {
const getBodyStatement = body => (body ? `\n\t$$\n${body}\n\t$$` : '');
const getCommentsStatement = text => (text ? `\n\tCOMMENT = '${text}'` : '');
const getNotNullStatement = isEnabled => (isEnabled ? '\n\tNOT NULL' : '');
const getIfNotExistStatement = ifNotExist => (ifNotExist ? ' IF NOT EXISTS' : '');

const userDefinedFunctions = udfs.map(udf =>
assignTemplates(templates.createUDF, {
Expand Down Expand Up @@ -209,6 +217,26 @@ module.exports = (baseProvider, options, app) => {
}),
);

const tagsStatements = tags.map(tag =>
assignTemplates(templates.createTag, {
orReplace: getOrReplaceStatement(tag.orReplace),
ifNotExist: getIfNotExistStatement(tag.ifNotExist),
allowedValues: getTagAllowedValues({ allowedValues: tag.allowedValues }),
name: getFullName(currentSchemaName, getName(isCaseSensitive, tag.name)),
comment: getCommentsStatement(tag.description),
}),
);

if (!_.isEmpty(schemaTags)) {
const schemaTagStatement = assignTemplates(templates.alterSchema, {
name: fullName,
operation: 'SET TAG',
options: getTagKeyValues({ tags: schemaTags, isCaseSensitive }),
});

tagsStatements.push(schemaTagStatement);
}

const statements = [];

if (databaseName) {
Expand All @@ -226,6 +254,7 @@ module.exports = (baseProvider, options, app) => {
...sequencesStatements,
...fileFormatsStatements,
...stagesStatements,
...tagsStatements,
].join('\n');
},

Expand Down Expand Up @@ -285,24 +314,28 @@ module.exports = (baseProvider, options, app) => {
const columnDefinitions = tableData.columns
.map(column => commentIfDeactivated(column.statement, column))
.join(',\n\t\t');
const tagsStatement = getTagStatement({
tags: tableData.tableTags,
isCaseSensitive: tableData.isCaseSensitive,
});

if (tableData.selectStatement) {
return assignTemplates(templates.createAsSelect, {
name: tableData.fullName,
selectStatement: tableData.selectStatement,
tableOptions: addOptions([clusterKeys, copyGrants]),
tableOptions: addOptions([clusterKeys, copyGrants, tagsStatement]),
});
} else if (tableData.cloneTableName) {
return assignTemplates(templates.createCloneTable, {
name: tableData.fullName,
source_table: getFullName(schemaName, tableData.cloneTableName),
tableOptions: addOptions([atOrBefore, copyGrants]),
tableOptions: addOptions([atOrBefore, copyGrants, tagsStatement]),
});
} else if (tableData.likeTableName) {
return assignTemplates(templates.createLikeTable, {
name: tableData.fullName,
source_table: getFullName(schemaName, tableData.likeTableName),
tableOptions: addOptions([clusterKeys, copyGrants]),
tableOptions: addOptions([clusterKeys, copyGrants, tagsStatement]),
});
} else if (tableData.external) {
const location = tableData.externalOptions.location
Expand All @@ -321,7 +354,16 @@ module.exports = (baseProvider, options, app) => {
return assignTemplates(templates.createExternalTable, {
name: tableData.fullName,
tableOptions: addOptions(
[partitionKeys, fileFormat, location, refreshOnCreate, autoRefresh, pattern, copyGrants],
[
partitionKeys,
fileFormat,
location,
refreshOnCreate,
autoRefresh,
pattern,
copyGrants,
tagsStatement,
],
comment,
),

Expand All @@ -339,7 +381,7 @@ module.exports = (baseProvider, options, app) => {
temporary: temporary,
transient: transient,
tableOptions: addOptions(
[clusterKeys, stageFileFormat, copyOptions, dataRetentionTime, copyGrants],
[clusterKeys, stageFileFormat, copyOptions, dataRetentionTime, copyGrants, tagsStatement],
comment,
),

Expand Down Expand Up @@ -367,6 +409,10 @@ module.exports = (baseProvider, options, app) => {
not_nul: !columnDefinition.nullable ? ' NOT NULL' : '',
inline_constraint: getInlineConstraint(columnDefinition),
comment: columnDefinition.comment ? ` COMMENT $$${columnDefinition.comment}$$` : '',
tag: getTagStatement({
tags: columnDefinition.columnTags,
isCaseSensitive: columnDefinition.isCaseSensitive,
}),
});
return { statement: columnStatement, isActivated: columnDefinition.isActivated };
},
Expand Down Expand Up @@ -462,6 +508,12 @@ module.exports = (baseProvider, options, app) => {
viewData.selectStatement ||
`SELECT \n\t${viewColumnsToString(tableColumns, isActivated)}\nFROM ${tables.join(' INNER JOIN ')}`;

const tagStatement = getTagStatement({
tags: viewData.viewTags,
isCaseSensitive: viewData.isCaseSensitive,
indent: '',
});

return assignTemplates(templates.createView, {
secure: viewData.secure ? ' SECURE' : '',
materialized: viewData.materialized ? ' MATERIALIZED' : '',
Expand All @@ -470,6 +522,7 @@ module.exports = (baseProvider, options, app) => {
copy_grants: viewData.copyGrants ? 'COPY GRANTS\n' : '',
comment: viewData.comment ? 'COMMENT=$$' + viewData.comment + '$$\n' : '',
select_statement: selectStatement,
tag: tagStatement ? tagStatement + '\n' : '',
});
},

Expand All @@ -486,7 +539,8 @@ module.exports = (baseProvider, options, app) => {
},

hydrateColumn({ columnDefinition, jsonSchema, dbData }) {
return Object.assign({}, columnDefinition, {
return {
...columnDefinition,
name: getName(jsonSchema.isCaseSensitive, columnDefinition.name),
isCaseSensitive: jsonSchema.isCaseSensitive,
timePrecision: Number(jsonSchema.tPrecision),
Expand Down Expand Up @@ -525,10 +579,11 @@ module.exports = (baseProvider, options, app) => {
uniqueKeyConstraintName: jsonSchema.uniqueKeyConstraintName,
primaryKey: jsonSchema.primaryKeyConstraintName ? false : columnDefinition.primaryKey,
expression: jsonSchema.expression,
});
columnTags: jsonSchema.columnTags ?? [],
};
},

hydrateSchema(containerData, { udfs, procedures, sequences, fileFormats, stages } = {}) {
hydrateSchema(containerData, { udfs, procedures, sequences, fileFormats, stages, tags } = {}) {
return {
schemaName: getName(containerData.isCaseSensitive, containerData.name),
isCaseSensitive: containerData.isCaseSensitive,
Expand All @@ -537,6 +592,7 @@ module.exports = (baseProvider, options, app) => {
transient: containerData.transient,
managedAccess: containerData.managedAccess,
dataRetention: containerData.DATA_RETENTION_TIME_IN_DAYS,
schemaTags: containerData.schemaTags,
udfs: Array.isArray(udfs)
? udfs
.map(udf =>
Expand Down Expand Up @@ -625,6 +681,19 @@ module.exports = (baseProvider, options, app) => {
)
.filter(stage => stage.name)
: [],
tags: Array.isArray(tags)
? tags
.map(tag =>
clean({
name: tag.name || undefined,
orReplace: tag.orReplace || undefined,
ifNotExist: tag.ifNotExist || undefined,
allowedValues: tag.allowedValues || undefined,
description: tag.description || undefined,
}),
)
.filter(tag => tag.name)
: [],
};
},

Expand Down Expand Up @@ -772,6 +841,7 @@ module.exports = (baseProvider, options, app) => {
isCaseSensitive: firstTab.isCaseSensitive,
}),
),
tableTags: firstTab.tableTags ?? [],
};
},

Expand All @@ -791,6 +861,7 @@ module.exports = (baseProvider, options, app) => {
secure: firstTab.secure,
materialized: firstTab.materialized,
fullName,
viewTags: firstTab.viewTags ?? [],
};
},

Expand Down
62 changes: 62 additions & 0 deletions forward_engineering/helpers/tagHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @typedef {{ id: string, tagName?: string, tagValue?: string }} Tag
*/

const { isEmpty } = require('lodash');

module.exports = ({ getName, toString }) => {
/**
* @param {{ tags: Tag[], isCaseSensitive: boolean, indent: string }}
* @returns {string}
*/
const getTagStatement = ({ tags, isCaseSensitive, indent = ' ' }) => {
if (isEmpty(tags)) {
return '';
}

const keyValues = getTagKeyValues({ tags, isCaseSensitive });

return `${indent}WITH TAG ( ${keyValues} )`;
};

/**
* @param {{ allowedValues: string[] }}
* @returns {string}
*/
const getTagAllowedValues = ({ allowedValues }) => {
if (isEmpty(allowedValues)) {
return '';
}

const values = allowedValues.map(({ value }) => toString(value)).join(', ');

return ` ALLOWED_VALUES ${values}`;
};

/**
* @param {{ tags: Tag[], isCaseSensitive: boolean }}
* @returns {string}
*/
const getTagKeyValues = ({ tags, isCaseSensitive }) => {
return tags
.filter(tag => tag.tagName)
.map(tag => `${getTagName({ tagName: tag.tagName, isCaseSensitive })} = ${toString(tag.tagValue)}`)
.join(', ');
};

/**
* @param {{ tagName: string, isCaseSensitive: boolean }}
* @returns {string}
*/
const getTagName = ({ tagName, isCaseSensitive }) => {
const hasSpace = /\s/.test(tagName);

return hasSpace ? getName(isCaseSensitive, tagName) : tagName;
};

return {
getTagStatement,
getTagAllowedValues,
getTagKeyValues,
};
};
Loading