Skip to content

Commit

Permalink
Add alter column
Browse files Browse the repository at this point in the history
  • Loading branch information
loicknuchel committed Oct 30, 2024
1 parent daf8532 commit 53b06c9
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 97 deletions.
10 changes: 7 additions & 3 deletions libs/parser-sql/src/postgresAst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type StatementAst = { meta: TokenInfo } & (AlterSchemaStatementAst | Alte
InsertIntoStatementAst | SelectStatementAst | SetStatementAst | ShowStatementAst | UpdateStatementAst)
export type AlterSchemaStatementAst = { kind: 'AlterSchema', token: TokenInfo, schema: IdentifierAst, action: { kind: 'Rename', token: TokenInfo, schema: IdentifierAst } | { kind: 'Owner', token: TokenInfo, role: SchemaRoleAst } }
export type AlterSequenceStatementAst = { kind: 'AlterSequence', token: TokenInfo, ifExists?: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, as?: SequenceTypeAst, start?: SequenceParamAst, increment?: SequenceParamAst, minValue?: SequenceParamOptAst, maxValue?: SequenceParamOptAst, cache?: SequenceParamAst, ownedBy?: SequenceOwnedByAst }
export type AlterTableStatementAst = { kind: 'AlterTable', token: TokenInfo, ifExists?: TokenInfo, only?: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, action: AlterTableActionAst }
export type AlterTableStatementAst = { kind: 'AlterTable', token: TokenInfo, ifExists?: TokenInfo, only?: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, actions: AlterTableActionAst[] }
export type BeginStatementAst = { kind: 'Begin', token: TokenInfo, object?: {kind: 'Work' | 'Transaction', token: TokenInfo}, modes?: TransactionModeAst[] }
export type CommentOnStatementAst = { kind: 'CommentOn', token: TokenInfo, object: { token: TokenInfo, kind: CommentObject }, schema?: IdentifierAst, parent?: IdentifierAst, entity: IdentifierAst, comment: StringAst | NullAst }
export type CommitStatementAst = { kind: 'Commit', token: TokenInfo, object?: { kind: 'Work' | 'Transaction', token: TokenInfo }, chain?: { token: TokenInfo, no?: TokenInfo } }
Expand Down Expand Up @@ -68,11 +68,15 @@ export type OrderByClauseAst = { token: TokenInfo, expressions: (ExpressionAst &
export type LimitClauseAst = { token: TokenInfo, value: IntegerAst | ParameterAst | ({ kind: 'All', token: TokenInfo }) }
export type OffsetClauseAst = { token: TokenInfo, value: IntegerAst | ParameterAst, rows?: { kind: 'Rows' | 'Row', token: TokenInfo } }
export type FetchClauseAst = { token: TokenInfo, first: { kind: 'First' | 'Next', token: TokenInfo }, value: IntegerAst | ParameterAst, rows: { kind: 'Rows' | 'Row', token: TokenInfo }, mode: { kind: 'Only' | 'WithTies', token: TokenInfo } }
export type AlterTableActionAst = AddColumnAst | AddConstraintAst | DropColumnAst | DropConstraintAst
export type AlterTableActionAst = AddColumnAst | AlterColumnAst | DropColumnAst | AddConstraintAst | DropConstraintAst
export type AddColumnAst = { kind: 'AddColumn', token: TokenInfo, ifNotExists?: TokenInfo, column: TableColumnAst }
export type AddConstraintAst = { kind: 'AddConstraint', token: TokenInfo, constraint: TableConstraintAst }
export type DropColumnAst = { kind: 'DropColumn', token: TokenInfo, ifExists?: TokenInfo, column: IdentifierAst }
export type AlterColumnAst = { kind: 'AlterColumn', token: TokenInfo, column: IdentifierAst, action: AlterColumnActionAst }
export type AddConstraintAst = { kind: 'AddConstraint', token: TokenInfo, constraint: TableConstraintAst, notValid?: TokenInfo }
export type DropConstraintAst = { kind: 'DropConstraint', token: TokenInfo, ifExists?: TokenInfo, constraint: IdentifierAst }
export type AlterColumnActionAst = AlterColumnDefaultAst | AlterColumnNotNullAst
export type AlterColumnDefaultAst = { kind: 'Default', action: { kind: 'Set' | 'Drop', token: TokenInfo }, token: TokenInfo, expression?: ExpressionAst }
export type AlterColumnNotNullAst = { kind: 'NotNull', action: { kind: 'Set' | 'Drop', token: TokenInfo }, token: TokenInfo }
export type TypeColumnAst = { name: IdentifierAst, type: ColumnTypeAst, collation?: { token: TokenInfo, name: IdentifierAst } }
export type IndexColumnAst = ExpressionAst & { collation?: { token: TokenInfo, name: IdentifierAst }, order?: SortOrderAst, nulls?: SortNullsAst }
export type TableColumnAst = { name: IdentifierAst, type: ColumnTypeAst, constraints?: TableColumnConstraintAst[] }
Expand Down
6 changes: 4 additions & 2 deletions libs/parser-sql/src/postgresBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ CREATE TABLE users (
CREATE INDEX ON users (role);
ALTER TABLE users ADD COLUMN created_at timestamp NOT NULL DEFAULT now();
ALTER TABLE users DROP CONSTRAINT users_name_uniq;
ALTER TABLE users ALTER COLUMN role SET DEFAULT 'guest';
COMMENT ON TABLE users IS 'List users';
COMMENT ON COLUMN users.name IS 'user name';
COMMENT ON COLUMN users.role IS 'user role';
Expand All @@ -34,6 +35,7 @@ CREATE TABLE cms.posts (
);
ALTER TABLE cms.posts DROP COLUMN created_at;
ALTER TABLE cms.posts ADD UNIQUE (title);
ALTER TABLE cms.posts ALTER COLUMN title SET NOT NULL;
COMMENT ON CONSTRAINT posts_author_fk ON cms.posts IS 'posts fk';
CREATE VIEW admins AS SELECT id, name FROM users WHERE role='admin';
Expand All @@ -48,7 +50,7 @@ COMMENT ON TYPE bug_status IS 'bug status';
attrs: [
{name: 'id', type: 'int'},
{name: 'name', type: 'varchar', default: 'anon', doc: 'user name'},
{name: 'role', type: 'varchar'},
{name: 'role', type: 'varchar', default: 'guest'},
{name: 'created_at', type: 'timestamp', default: '`now()`'},
],
pk: {attrs: [['id']]},
Expand All @@ -61,7 +63,7 @@ COMMENT ON TYPE bug_status IS 'bug status';
name: 'posts',
attrs: [
{name: 'id', type: 'int'},
{name: 'title', type: 'varchar', null: true},
{name: 'title', type: 'varchar'},
{name: 'author', type: 'int', null: true},
],
pk: {attrs: [['id']]},
Expand Down
113 changes: 63 additions & 50 deletions libs/parser-sql/src/postgresBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,58 +261,71 @@ function createIndex(index: number, stmt: CreateIndexStatementAst, entities: Ent
function alterTable(index: number, stmt: AlterTableStatementAst, db: Database): void {
const entity = db.entities?.find(e => e.schema === stmt.schema?.value && e.name === stmt.table?.value)
if (entity) {
const action = stmt.action
if (action.kind === 'AddColumn') {
if (!entity.attrs) entity.attrs = []
const exists = entity.attrs.find(a => a.name === action.column.name.value)
if (!exists) entity.attrs.push(buildTableAttr(index, action.column))
} else if (action.kind === 'DropColumn') {
const attrIndex = entity.attrs?.findIndex(a => a.name === action.column.value)
if (attrIndex !== undefined && attrIndex !== -1) entity.attrs?.splice(attrIndex, 1)
// TODO: remove constraints depending on this column
} else if (action.kind === 'AddConstraint') {
const constraint = action.constraint
if (constraint.kind === 'PrimaryKey') {
entity.pk = removeUndefined({name: constraint.constraint?.name.value, attrs: constraint.columns.map(c => [c.value]), extra: {line: stmt.token.position.start.line, statement: index}})
} else if (constraint.kind === 'Unique') {
if (!entity.indexes) entity.indexes = []
entity.indexes.push(removeUndefined({
name: constraint.constraint?.name.value,
attrs: constraint.columns.map(c => [c.value]),
unique: true,
extra: {line: stmt.token.position.start.line, statement: index},
}))
} else if (constraint.kind === 'Check') {
if (!entity.checks) entity.checks = []
entity.checks.push(removeUndefined({
name: constraint.constraint?.name.value,
attrs: expressionAttrs(constraint.predicate),
predicate: expressionToString(constraint.predicate),
extra: {line: stmt.token.position.start.line, statement: index},
}))
} else if (constraint.kind === 'ForeignKey') {
if (!db.relations) db.relations = []
db.relations.push(removeUndefined({
name: constraint.constraint?.name.value,
src: removeUndefined({schema: stmt.schema?.value, entity: stmt.table?.value, attrs: constraint.columns.map(c => [c.value])}),
ref: removeUndefined({schema: constraint.ref.schema?.value, entity: constraint.ref.table.value, attrs: constraint.ref.columns?.map(c => [c.value]) || []}),
extra: {line: stmt.token.position.start.line, statement: index},
}))
stmt.actions.forEach(action => {
if (action.kind === 'AddColumn') {
if (!entity.attrs) entity.attrs = []
const exists = entity.attrs.find(a => a.name === action.column.name.value)
if (!exists) entity.attrs.push(buildTableAttr(index, action.column))
} else if (action.kind === 'DropColumn') {
const attrIndex = entity.attrs?.findIndex(a => a.name === action.column.value)
if (attrIndex !== undefined && attrIndex !== -1) entity.attrs?.splice(attrIndex, 1)
// TODO: remove constraints depending on this column
} else if (action.kind === 'AlterColumn') {
const attr = entity.attrs?.find(a => a.name === action.column.value)
if (attr) {
const aa = action.action
if (aa.kind === 'Default') {
attr.default = aa.action.kind === 'Set' && aa.expression ? expressionToValue(aa.expression) : undefined
} else if (aa.kind === 'NotNull') {
attr.null = aa.action.kind === 'Set' ? undefined : true
} else {
isNever(aa)
}
}
} else if (action.kind === 'AddConstraint') {
const constraint = action.constraint
if (constraint.kind === 'PrimaryKey') {
entity.pk = removeUndefined({name: constraint.constraint?.name.value, attrs: constraint.columns.map(c => [c.value]), extra: {line: stmt.token.position.start.line, statement: index}})
} else if (constraint.kind === 'Unique') {
if (!entity.indexes) entity.indexes = []
entity.indexes.push(removeUndefined({
name: constraint.constraint?.name.value,
attrs: constraint.columns.map(c => [c.value]),
unique: true,
extra: {line: stmt.token.position.start.line, statement: index},
}))
} else if (constraint.kind === 'Check') {
if (!entity.checks) entity.checks = []
entity.checks.push(removeUndefined({
name: constraint.constraint?.name.value,
attrs: expressionAttrs(constraint.predicate),
predicate: expressionToString(constraint.predicate),
extra: {line: stmt.token.position.start.line, statement: index},
}))
} else if (constraint.kind === 'ForeignKey') {
if (!db.relations) db.relations = []
db.relations.push(removeUndefined({
name: constraint.constraint?.name.value,
src: removeUndefined({schema: stmt.schema?.value, entity: stmt.table?.value, attrs: constraint.columns.map(c => [c.value])}),
ref: removeUndefined({schema: constraint.ref.schema?.value, entity: constraint.ref.table.value, attrs: constraint.ref.columns?.map(c => [c.value]) || []}),
extra: {line: stmt.token.position.start.line, statement: index},
}))
} else {
isNever(constraint)
}
} else if (action.kind === 'DropConstraint') {
if (entity.pk?.name === action.constraint.value) entity.pk = undefined
const idxIndex = entity.indexes?.findIndex(a => a.name === action.constraint.value)
if (idxIndex !== undefined && idxIndex !== -1) entity.indexes?.splice(idxIndex, 1)
const chkIndex = entity.checks?.findIndex(c => c.name === action.constraint.value)
if (chkIndex !== undefined && chkIndex !== -1) entity.checks?.splice(chkIndex, 1)
const relIndex = db.relations?.findIndex(r => r.name === action.constraint.value && r.src.schema === stmt.schema?.value && r.src.entity === stmt.table?.value)
if (relIndex !== undefined && relIndex !== -1) db.relations?.splice(relIndex, 1)
// TODO: also NOT NULL & DEFAULT constraints...
} else {
isNever(constraint)
isNever(action)
}
} else if (action.kind === 'DropConstraint') {
if (entity.pk?.name === action.constraint.value) entity.pk = undefined
const idxIndex = entity.indexes?.findIndex(a => a.name === action.constraint.value)
if (idxIndex !== undefined && idxIndex !== -1) entity.indexes?.splice(idxIndex, 1)
const chkIndex = entity.checks?.findIndex(c => c.name === action.constraint.value)
if (chkIndex !== undefined && chkIndex !== -1) entity.checks?.splice(chkIndex, 1)
const relIndex = db.relations?.findIndex(r => r.name === action.constraint.value && r.src.schema === stmt.schema?.value && r.src.entity === stmt.table?.value)
if (relIndex !== undefined && relIndex !== -1) db.relations?.splice(relIndex, 1)
// TODO: also NOT NULL & DEFAULT constraints...
} else {
isNever(action)
}
})
}
}

Expand Down
Loading

0 comments on commit 53b06c9

Please sign in to comment.