diff --git a/pegjs/postgresql.pegjs b/pegjs/postgresql.pegjs index f1d8ba7d..342f31d3 100644 --- a/pegjs/postgresql.pegjs +++ b/pegjs/postgresql.pegjs @@ -817,7 +817,7 @@ create_table_stmt type: a[0].toLowerCase(), keyword: 'table', temporary: tp && tp[0].toLowerCase(), - if_not_exists:ife, + if_not_exists: ife, table: t, partition_of: po } @@ -1672,12 +1672,17 @@ alter_schema_stmt alter_table_stmt = KW_ALTER __ KW_TABLE __ + ife:if_exists? __ + o:'only'i? __ t:table_ref_list __ e:alter_action_list { /* export interface alter_table_stmt_node { type: 'alter'; table: table_ref_list; + keyword: 'table'; + if_exists: if_exists; + prefix?: literal_string; expr: alter_action_list; } => AstStatement @@ -1688,6 +1693,9 @@ alter_table_stmt columnList: columnListTableAlias(columnList), ast: { type: 'alter', + keyword: 'table', + if_exists: ife, + prefix: o && { type: 'origin', value: o }, table: t, expr: e } @@ -1709,21 +1717,27 @@ alter_action / ALTER_RENAME / ALTER_ALGORITHM / ALTER_LOCK + / ALTER_COLUMN_DATA_TYPE + / ALTER_COLUMN_DEFAULT + / ALTER_COLUMN_NOT_NULL ALTER_ADD_COLUMN = KW_ADD __ kc:KW_COLUMN? __ + ife:if_not_exists_stmt? __ cd:create_column_definition { /* => { action: 'add'; keyword: KW_COLUMN; resource: 'column'; + if_not_exists: ife; type: 'alter'; } & create_column_definition; */ return { action: 'add', + if_not_exists: ife, ...cd, keyword: kc, resource: 'column', @@ -1734,17 +1748,20 @@ ALTER_ADD_COLUMN ALTER_DROP_COLUMN = KW_DROP __ kc:KW_COLUMN? __ + ife:if_exists? __ c:column_ref { /* => { action: 'drop'; collumn: column_ref; keyword: KW_COLUMN; + if_exists: if_exists; resource: 'column'; type: 'alter'; } */ return { action: 'drop', column: c, + if_exists: ife, keyword: kc, resource: 'column', type: 'alter', @@ -1863,6 +1880,89 @@ ALTER_LOCK } } +ALTER_COLUMN_DATA_TYPE + = KW_ALTER __ kc:KW_COLUMN? __ c:column_ref __ sd:(KW_SET __ 'data'i)? __ 'type'i __ t:data_type __ co:collate_expr? __ us:(KW_USING __ expr)? { + /* + => { + action: 'alter'; + keyword?: KW_COLUMN; + using?: expr; + type: 'alter'; + } & create_column_definition; + */ + c.suffix = sd ? 'set data type' : 'type' + return { + action: 'alter', + column: c, + keyword: kc, + resource: 'column', + definition: t, + collate: co, + using: us && us[2], + type: 'alter', + } + } + +ALTER_COLUMN_DEFAULT + = KW_ALTER __ kc:KW_COLUMN? __ c:column_ref __ KW_SET __ KW_DEFAULT __ e:expr { + /* => { + action: 'alter'; + keyword?: KW_COLUMN; + default_val?: { type: 'set default', value: expr }; + type: 'alter'; + } & create_column_definition; + */ + return { + action: 'alter', + column: c, + keyword: kc, + resource: 'column', + default_val: { + type: 'set default', + value: e, + }, + type: 'alter', + } + } + / KW_ALTER __ kc:KW_COLUMN? __ c:column_ref __ KW_DROP __ KW_DEFAULT { + /* => { + action: 'alter'; + keyword?: KW_COLUMN; + default_val?: { type: 'set default', value: expr }; + type: 'alter'; + } & create_column_definition; + */ + return { + action: 'alter', + column: c, + keyword: kc, + resource: 'column', + default_val: { + type: 'drop default', + }, + type: 'alter', + } + } + +ALTER_COLUMN_NOT_NULL + = KW_ALTER __ kc:KW_COLUMN? __ c:column_ref __ ac:(KW_SET / KW_DROP) __ n:literal_not_null { + /* => { + action: 'alter'; + keyword?: KW_COLUMN; + nullable: literal_not_null; + type: 'alter'; + } & create_column_definition; + */ + n.action = ac.toLowerCase(); + return { + action: 'alter', + column: c, + keyword: kc, + resource: 'column', + nullable: n, + type: 'alter', + } + } create_index_definition = kc:(KW_INDEX / KW_KEY) __ c:column? __ diff --git a/src/alter.js b/src/alter.js index b607b7ce..79ec14a3 100644 --- a/src/alter.js +++ b/src/alter.js @@ -4,14 +4,15 @@ import { indexTypeAndOptionToSQL } from './index-definition' import { tablesToSQL, tableToSQL } from './tables' import { exprToSQL } from './expr' import { selectToSQL } from './select' -import { dataTypeToSQL, hasVal, toUpper, identifierToSql } from './util' +import { dataTypeToSQL, hasVal, toUpper, identifierToSql, literalToSQL } from './util' function alterExprToSQL(expr) { if (!expr) return '' const { action, create_definitions: createDefinition, - if_not_exists: ifNotExists,keyword, + if_not_exists: ifNotExists, keyword, + if_exists: ifExists, old_column: oldColumn, prefix, resource, @@ -58,6 +59,7 @@ function alterExprToSQL(expr) { toUpper(action), toUpper(keyword), toUpper(ifNotExists), + toUpper(ifExists), oldColumn && columnRefToSQL(oldColumn), toUpper(prefix), name && name.trim(), @@ -68,11 +70,11 @@ function alterExprToSQL(expr) { } function alterTableToSQL(stmt) { - const { type, table, expr = [] } = stmt + const { type, table, if_exists, prefix, expr = [] } = stmt const action = toUpper(type) const tableName = tablesToSQL(table) const exprList = expr.map(exprToSQL) - const result = [action, 'TABLE', tableName, exprList.join(', ')] + const result = [action, 'TABLE', toUpper(if_exists), literalToSQL(prefix), tableName, exprList.join(', ')] return result.filter(hasVal).join(' ') } diff --git a/src/column.js b/src/column.js index 5012655c..2b2b0d2a 100644 --- a/src/column.js +++ b/src/column.js @@ -96,7 +96,7 @@ function columnReferenceDefinitionToSQL(referenceDefinition) { function columnOption(definition) { const columnOpt = [] const { - nullable, character_set: characterSet, check, comment, collate, storage, + nullable, character_set: characterSet, check, comment, collate, storage, using, default_val: defaultOpt, auto_increment: autoIncrement, unique: uniqueKey, @@ -105,7 +105,7 @@ function columnOption(definition) { reference_definition: referenceDefinition, } = definition - columnOpt.push(toUpper(nullable && nullable.value)) + columnOpt.push(toUpper(nullable && nullable.action), toUpper(nullable && nullable.value)) if (defaultOpt) { const { type, value } = defaultOpt columnOpt.push(type.toUpperCase(), exprToSQL(value)) @@ -118,6 +118,7 @@ function columnOption(definition) { columnOpt.push(...commonTypeValue(columnFormat)) columnOpt.push(...commonTypeValue(storage)) columnOpt.push(...columnReferenceDefinitionToSQL(referenceDefinition)) + columnOpt.push(commonOptionConnector('USING', exprToSQL, using)) return columnOpt.filter(hasVal).join(' ') } diff --git a/test/postgres.spec.js b/test/postgres.spec.js index 9fd00c45..23d9761e 100644 --- a/test/postgres.spec.js +++ b/test/postgres.spec.js @@ -865,8 +865,8 @@ describe('Postgres', () => { { title: 'alter table add constraint', sql: [ - 'ALTER TABLE address ADD CONSTRAINT user_id_address_fk FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE ON UPDATE RESTRICT;', - 'ALTER TABLE "address" ADD CONSTRAINT "user_id_address_fk" FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE CASCADE ON UPDATE RESTRICT' + 'ALTER TABLE if exists only address ADD CONSTRAINT user_id_address_fk FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE ON UPDATE RESTRICT;', + 'ALTER TABLE IF EXISTS ONLY "address" ADD CONSTRAINT "user_id_address_fk" FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE CASCADE ON UPDATE RESTRICT' ] }, { @@ -1540,7 +1540,28 @@ describe('Postgres', () => { LIMIT 1;`, `SELECT somefunc("engineering_networks".realizaciya, "engineering_networks".company = 'blah-blah' AND "engineering_networks".obem_realizacii_tip = 'uslugi') AS "var0", If(var0 > 0, '2', '1') AS "fontColor" FROM "engineering_networks" AS "engineering_networks" WHERE "engineering_networks".company = 'blah-blah' AND "engineering_networks".month IN ('April') AND "engineering_networks".year IN ('2024') LIMIT 1` ], - } + }, + { + title: 'alter column data type', + sql: [ + `ALTER TABLE employees ALTER COLUMN first_name SET DATA TYPE TEXT COLLATE "C" using upper(first_name);`, + 'ALTER TABLE "employees" ALTER COLUMN first_name SET DATA TYPE TEXT COLLATE "C" USING upper(first_name)' + ] + }, + { + title: 'alter column set and drop default', + sql: [ + "ALTER TABLE transactions ADD COLUMN status varchar(30) DEFAULT 'old', ALTER COLUMN status SET default 'current', ALTER COLUMN name drop default;", + `ALTER TABLE "transactions" ADD COLUMN status VARCHAR(30) DEFAULT 'old', ALTER COLUMN status SET DEFAULT 'current', ALTER COLUMN name DROP DEFAULT`, + ] + }, + { + title: 'alter column set not null', + sql: [ + 'ALTER TABLE transactions ALTER COLUMN status SET NOT NULL', + 'ALTER TABLE "transactions" ALTER COLUMN status SET NOT NULL', + ] + }, ] function neatlyNestTestedSQL(sqlList){ sqlList.forEach(sqlInfo => {