Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/** @import {SchemaSequence} from '../../../types/ddlProvider' */

const trim = require('lodash/trim');
const templates = require('../../templates');
const { assignTemplates } = require('../../../utils/assignTemplates');
const { getNamePrefixedWithSchemaName, setTab } = require('../../../utils/general');

const DEFAULT_CACHE_SIZE = 20;

/**
* Check whether a numeric field is present, including 0.
*
* @param {{ value?: number }} params Value to test.
* @returns {boolean} Whether the value is a number.
*/
const isPresentNumber = ({ value }) => typeof value === 'number' && !Number.isNaN(value);

/**
* Check whether a sequence row has a usable name.
*
* @param {{ sequenceName?: string }} params Sequence name.
* @returns {boolean} Whether the name is non-blank.
*/
const hasSequenceName = ({ sequenceName }) => Boolean(trim(sequenceName ?? ''));

/**
* Build the AS clause.
*
* @param {{ dataType?: string; precision?: number }} params Type fields.
* @returns {string} AS clause, or empty.
*/
const getAsClause = ({ dataType, precision }) => {
if (!dataType) {
return '';
}

if (dataType === 'DECIMAL' && isPresentNumber({ value: precision })) {
return `AS DECIMAL(${precision},0)`;
}

return `AS ${dataType}`;
};

/**
* Build a keyword-plus-number clause, treating 0 as present.
*
* @param {{ keyword: string; value?: number }} params Clause parts.
* @returns {string} Clause, or empty.
*/
const getNumericClause = ({ keyword, value }) => {
return isPresentNumber({ value }) ? `${keyword} ${value}` : '';
};

/**
* Build MINVALUE / MAXVALUE or the matching NO-* clause.
*
* @param {{ noBound?: boolean; value?: number; noKeyword: string; valueKeyword: string }} params Bound fields.
* @returns {string} Bound clause, or empty.
*/
const getBoundClause = ({ noBound, value, noKeyword, valueKeyword }) => {
if (noBound) {
return noKeyword;
}

return getNumericClause({ keyword: valueKeyword, value });
};

/**
* Build the CACHE / NO CACHE clause.
*
* @param {{ cache?: string; cacheValue?: number }} params Cache fields.
* @returns {string} Cache clause, or empty.
*/
const getCacheClause = ({ cache, cacheValue }) => {
if (cache === 'CACHE') {
const size = isPresentNumber({ value: cacheValue }) ? cacheValue : DEFAULT_CACHE_SIZE;
return `CACHE ${size}`;
}

if (!cache) {
return '';
}

return cache;
};

/**
* Build CREATE SEQUENCE clauses in IBM order.
*
* @param {{ sequence: SchemaSequence }} params Sequence row.
* @returns {string[]} Non-empty clauses.
*/
const getSequenceClauses = ({ sequence }) => {
return [
getAsClause({ dataType: sequence.dataType, precision: sequence.precision }),
getNumericClause({ keyword: 'START WITH', value: sequence.start }),
getNumericClause({ keyword: 'INCREMENT BY', value: sequence.increment }),
getBoundClause({
noBound: sequence.noMinValue,
value: sequence.minValue,
noKeyword: 'NO MINVALUE',
valueKeyword: 'MINVALUE',
}),
getBoundClause({
noBound: sequence.noMaxValue,
value: sequence.maxValue,
noKeyword: 'NO MAXVALUE',
valueKeyword: 'MAXVALUE',
}),
sequence.cycle ?? '',
getCacheClause({ cache: sequence.cache, cacheValue: sequence.cacheValue }),
sequence.order ?? '',
].filter(Boolean);
};

/**
* Emit one CREATE SEQUENCE statement.
*
* @param {{ schemaName: string; sequence: SchemaSequence }} params Schema and row.
* @returns {string} CREATE SEQUENCE DDL.
*/
const createSequenceScript = ({ schemaName, sequence }) => {
const name = getNamePrefixedWithSchemaName({ name: sequence.sequenceName ?? '', schemaName });
const clauses = getSequenceClauses({ sequence });
const clausesBlock = clauses.length === 0 ? '' : `\n${setTab({ text: clauses.join('\n') })}`;

return assignTemplates({
template: templates.createSequence,
templateData: {
name,
clauses: clausesBlock,
},
});
};

/**
* Emit CREATE SEQUENCE statements for a schema’s sequence group.
*
* @param {{ schemaName?: string; sequences?: SchemaSequence[] }} params Hydrated schema fields.
* @returns {string} Joined CREATE SEQUENCE DDL, or empty.
*/
const getSequencesScript = ({ schemaName = '', sequences = [] }) => {
const sequenceRows = Array.isArray(sequences) ? sequences : [];

return sequenceRows
.filter(sequence => hasSequenceName({ sequenceName: sequence.sequenceName }))
.map(sequence => createSequenceScript({ schemaName, sequence }))
.join('\n\n');
};

module.exports = {
getSequencesScript,
};
22 changes: 21 additions & 1 deletion forward_engineering/ddlProvider/ddlProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* @import {
* CheckConstraintInput,
* ContainerData,
* ContainerLevelPreparedData,
* CreateSchemaParams,
* CreateTableParams,
* DdlProvider,
Expand Down Expand Up @@ -62,6 +63,7 @@ const { joinActivatedAndDeactivatedStatements } = require('../utils/joinActivate
const { getIndexName } = require('./ddlHelpers/index/getIndexName.js');
const { getIndexType } = require('./ddlHelpers/index/getIndexType.js');
const { getIndexOptions } = require('./ddlHelpers/index/getIndexOptions.js');
const { getSequencesScript } = require('./ddlHelpers/sequence/getSequencesScript.js');
const { getNumericValue } = require('../utils/general.js');

/**
Expand Down Expand Up @@ -121,12 +123,14 @@ const providerHasType = type => hasType({ descriptors, type });
* Hydrate schema data.
*
* @param {ContainerData} containerData Container data.
* @param {ContainerLevelPreparedData} [data] Prepared container-level data from Studio.
* @returns {SchemaData} Hydrated schema.
*/
const hydrateSchema = containerData => ({
const hydrateSchema = (containerData, data) => ({
schemaName: containerData.name,
isActivated: containerData.isActivated,
description: containerData.description,
sequences: data?.sequences,
});

/**
Expand All @@ -150,6 +154,21 @@ const createSchema = ({ schemaName, isActivated = true }) => {
return commentDeactivatedStatement(setSchemaStatement + '\n', { isActivated });
};

/**
* Create schema-level CREATE SEQUENCE statements.
*
* @param {SchemaData} schemaData Hydrated schema.
* @returns {string} CREATE SEQUENCE DDL, or empty.
*/
const createSchemaSequences = ({ schemaName, sequences, isActivated = true }) => {
const script = getSequencesScript({ schemaName, sequences });
if (!script) {
return '';
}

return commentDeactivatedStatement(script, { isActivated, isPartOfLine: false });
};

/**
* Return no DDL for dropping a schema. Db2 for z/OS schemas are qualifiers rather than standalone objects that can be
* dropped. This method remains in the provider for framework compatibility.
Expand Down Expand Up @@ -980,6 +999,7 @@ module.exports = (_baseProvider, _options, _app) => ({
hasType: providerHasType,
hydrateSchema,
createSchema,
createSchemaSequences,
dropSchema,
alterSchema,
createUdt,
Expand Down
2 changes: 2 additions & 0 deletions forward_engineering/ddlProvider/templates.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module.exports = {
setSchema: 'SET SCHEMA = ${schemaName};',

createSequence: 'CREATE SEQUENCE ${name}${clauses};',

createType: 'CREATE DISTINCT TYPE ${name} AS ${sourceType};',

dropType: 'DROP TYPE ${name};',
Expand Down
24 changes: 23 additions & 1 deletion forward_engineering/types/ddlProvider.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,31 @@ export type HydratedColumn = {
itemsType?: string;
};

export type SchemaSequence = {
sequenceName?: string;
dataType?: string;
precision?: number;
start?: number;
increment?: number;
noMinValue?: boolean;
minValue?: number;
noMaxValue?: boolean;
maxValue?: number;
cycle?: string;
cache?: string;
cacheValue?: number;
order?: string;
};

export type ContainerLevelPreparedData = {
sequences?: SchemaSequence[];
};

export type SchemaData = {
schemaName: string;
isActivated?: boolean;
description?: string;
sequences?: SchemaSequence[];
};

export type ContainerData = {
Expand Down Expand Up @@ -739,8 +760,9 @@ export type DdlProvider = {
getTypesDescriptors(): TypeDescriptors;
hasType(type: string): boolean;

hydrateSchema(containerData: ContainerData, data?: unknown): SchemaData;
hydrateSchema(containerData: ContainerData, data?: ContainerLevelPreparedData): SchemaData;
createSchema(params: CreateSchemaParams): string;
createSchemaSequences(schemaData: SchemaData): string;
dropSchema(params: DropSchemaParams): string;
alterSchema(schemaName: string, data?: unknown): string;
createUdt(udt: HydratedColumn, dbData?: unknown): string;
Expand Down
Loading
Loading