diff --git a/libs/parser-sql/src/postgresAst.ts b/libs/parser-sql/src/postgresAst.ts index b69de3d88..0f364e678 100644 --- a/libs/parser-sql/src/postgresAst.ts +++ b/libs/parser-sql/src/postgresAst.ts @@ -18,112 +18,137 @@ export type StatementAst = { meta: TokenInfo } & (AlterSchemaStatementAst | Alte CreateMaterializedViewStatementAst | CreateSchemaStatementAst | CreateSequenceStatementAst | CreateTableStatementAst | CreateTriggerStatementAst | CreateTypeStatementAst | CreateViewStatementAst | DeleteStatementAst | DropStatementAst | 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, 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 } } -export type CreateExtensionStatementAst = { kind: 'CreateExtension', token: TokenInfo, ifNotExists?: TokenInfo, name: IdentifierAst, with?: TokenInfo, schema?: { token: TokenInfo, name: IdentifierAst }, version?: { token: TokenInfo, number: StringAst | IdentifierAst }, cascade?: TokenInfo } -export type CreateFunctionStatementAst = { kind: 'CreateFunction', token: TokenInfo, replace?: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, args: FunctionArgumentAst[], returns?: FunctionReturnsAst, language?: { token: TokenInfo, name: IdentifierAst }, behavior?: { kind: 'Immutable' | 'Stable' | 'Volatile', token: TokenInfo }, nullBehavior?: { kind: 'Called' | 'ReturnsNull' | 'Strict', token: TokenInfo }, definition?: { token: TokenInfo, value: StringAst }, return?: { token: TokenInfo, expression: ExpressionAst } } -export type CreateIndexStatementAst = { kind: 'CreateIndex', token: TokenInfo, unique?: TokenInfo, concurrently?: TokenInfo, ifNotExists?: TokenInfo, name?: IdentifierAst, only?: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, using?: { token: TokenInfo, method: IdentifierAst }, columns: IndexColumnAst[], include?: { token: TokenInfo, columns: IdentifierAst[] }, where?: WhereClauseAst } -export type CreateMaterializedViewStatementAst = { kind: 'CreateMaterializedView', token: TokenInfo, ifNotExists?: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, columns?: IdentifierAst[], query: SelectStatementInnerAst, withData?: { token: TokenInfo, no?: TokenInfo } } -export type CreateSchemaStatementAst = { kind: 'CreateSchema', token: TokenInfo, ifNotExists?: TokenInfo, schema?: IdentifierAst, authorization?: { token: TokenInfo, role: SchemaRoleAst } } -export type CreateSequenceStatementAst = { kind: 'CreateSequence', token: TokenInfo, mode?: SequenceModeAst, ifNotExists?: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, as?: SequenceTypeAst, start?: SequenceParamAst, increment?: SequenceParamAst, minValue?: SequenceParamOptAst, maxValue?: SequenceParamOptAst, cache?: SequenceParamAst, ownedBy?: SequenceOwnedByAst } -export type CreateTableStatementAst = { kind: 'CreateTable', token: TokenInfo, mode?: CreateTableModeAst, ifNotExists?: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, columns?: TableColumnAst[], constraints?: TableConstraintAst[] } -export type CreateTriggerStatementAst = { kind: 'CreateTrigger', token: TokenInfo, replace?: TokenInfo, constraint?: { token: TokenInfo }, name: IdentifierAst, timing: TriggerTimingAst, events?: TriggerEventAst[], schema?: IdentifierAst, table: IdentifierAst, from?: { token: TokenInfo, schema?: IdentifierAst, table: IdentifierAst }, deferrable?: TriggerDeferrableAst, referencing?: TriggerReferencingAst, target?: { kind: 'Row' | 'Statement', token: TokenInfo }, when?: { token: TokenInfo, condition: ExpressionAst }, execute: { token: TokenInfo, schema?: IdentifierAst, function: IdentifierAst, arguments: ExpressionAst[] } } -export type CreateTypeStatementAst = { kind: 'CreateType', token: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, struct?: { token: TokenInfo, attrs: TypeColumnAst[] }, enum?: { token: TokenInfo, values: StringAst[] }, base?: { name: IdentifierAst, value: ExpressionAst }[] } -export type CreateViewStatementAst = { kind: 'CreateView', token: TokenInfo, replace?: TokenInfo, temporary?: TokenInfo, recursive?: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, columns?: IdentifierAst[], query: SelectStatementInnerAst } -export type DeleteStatementAst = { kind: 'Delete', token: TokenInfo, only?: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, descendants?: TokenInfo, alias?: AliasAst, using?: FromItemAst & { token: TokenInfo }, where?: WhereClauseAst, returning?: SelectClauseAst } -export type DropStatementAst = { kind: 'Drop', token: TokenInfo, object: DropObject, entities: ObjectNameAst[], concurrently?: TokenInfo, ifExists?: TokenInfo, mode?: { kind: DropMode, token: TokenInfo } } -export type InsertIntoStatementAst = { kind: 'InsertInto', token: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, columns?: IdentifierAst[], values: (ExpressionAst | { kind: 'Default', token: TokenInfo })[][], onConflict?: OnConflictClauseAst, returning?: SelectClauseAst } -export type SelectStatementAst = { kind: 'Select' } & SelectStatementInnerAst -export type SetStatementAst = { kind: 'Set', token: TokenInfo, scope?: { kind: SetScope, token: TokenInfo }, parameter: IdentifierAst, equal?: { kind: SetAssign, token: TokenInfo }, value: SetValueAst } +export type AlterSchemaStatementAst = { kind: 'AlterSchema', token: TokenInfo, schema: IdentifierAst, action: SchemaAlterActionAst } +export type AlterSequenceStatementAst = { kind: 'AlterSequence', token: TokenInfo, ifExists?: TokenAst, 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?: TokenAst, only?: TokenAst, schema?: IdentifierAst, table: IdentifierAst, actions: TableAlterActionAst[] } +export type BeginStatementAst = { kind: 'Begin', token: TokenInfo, object?: TransactionObjectAst, modes?: TransactionModeAst[] } +export type CommentOnStatementAst = { kind: 'CommentOn', token: TokenInfo, object: CommentObjectAst, schema?: IdentifierAst, parent?: IdentifierAst, entity: IdentifierAst, comment: StringAst | NullAst } +export type CommitStatementAst = { kind: 'Commit', token: TokenInfo, object?: TransactionObjectAst, chain?: TransactionChainAst } +export type CreateExtensionStatementAst = { kind: 'CreateExtension', token: TokenInfo, ifNotExists?: TokenAst, name: IdentifierAst, with?: TokenAst, schema?: NameAst, version?: ExtensionVersionAst, cascade?: TokenAst } +export type CreateFunctionStatementAst = { kind: 'CreateFunction', token: TokenInfo, replace?: TokenAst, schema?: IdentifierAst, name: IdentifierAst, args: FunctionArgumentAst[], returns?: FunctionReturnsAst, language?: NameAst, behavior?: FunctionBehaviorAst, nullBehavior?: FunctionNullBehaviorAst, definition?: FunctionDefinitionAst, return?: FilterAst } +export type CreateIndexStatementAst = { kind: 'CreateIndex', token: TokenInfo, unique?: TokenAst, concurrently?: TokenAst, ifNotExists?: TokenAst, name?: IdentifierAst, only?: TokenAst, schema?: IdentifierAst, table: IdentifierAst, using?: IndexUsingAst, columns: IndexColumnAst[], include?: IndexIncludeAst, where?: WhereClauseAst } +export type CreateMaterializedViewStatementAst = { kind: 'CreateMaterializedView', token: TokenInfo, ifNotExists?: TokenAst, schema?: IdentifierAst, name: IdentifierAst, columns?: IdentifierAst[], query: SelectInnerAst, withData?: ViewMaterializedDataAst } +export type CreateSchemaStatementAst = { kind: 'CreateSchema', token: TokenInfo, ifNotExists?: TokenAst, schema?: IdentifierAst, authorization?: SchemaAuthorizationAst } +export type CreateSequenceStatementAst = { kind: 'CreateSequence', token: TokenInfo, mode?: SequenceModeAst, ifNotExists?: TokenAst, schema?: IdentifierAst, name: IdentifierAst, as?: SequenceTypeAst, start?: SequenceParamAst, increment?: SequenceParamAst, minValue?: SequenceParamOptAst, maxValue?: SequenceParamOptAst, cache?: SequenceParamAst, ownedBy?: SequenceOwnedByAst } +export type CreateTableStatementAst = { kind: 'CreateTable', token: TokenInfo, mode?: TableCreateModeAst, ifNotExists?: TokenAst, schema?: IdentifierAst, name: IdentifierAst, columns?: TableColumnAst[], constraints?: TableConstraintAst[] } +export type CreateTriggerStatementAst = { kind: 'CreateTrigger', token: TokenInfo, replace?: TokenAst, constraint?: TokenAst, name: IdentifierAst, timing: TriggerTimingAst, events?: TriggerEventAst[], schema?: IdentifierAst, table: IdentifierAst, from?: TriggerFromAst, deferrable?: TriggerDeferrableAst, referencing?: TriggerReferencingAst, target?: TriggerTargetAst, when?: FilterAst, execute: TriggerExecuteAst } +export type CreateTypeStatementAst = { kind: 'CreateType', token: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, struct?: TypeStructAst, enum?: TypeEnumAst, base?: TypeBaseAttrAst[] } +export type CreateViewStatementAst = { kind: 'CreateView', token: TokenInfo, replace?: TokenAst, temporary?: TokenAst, recursive?: TokenAst, schema?: IdentifierAst, name: IdentifierAst, columns?: IdentifierAst[], query: SelectInnerAst } +export type DeleteStatementAst = { kind: 'Delete', token: TokenInfo, only?: TokenAst, schema?: IdentifierAst, table: IdentifierAst, descendants?: TokenAst, alias?: AliasAst, using?: FromClauseItemAst & TokenAst, where?: WhereClauseAst, returning?: SelectClauseAst } +export type DropStatementAst = { kind: 'Drop', token: TokenInfo, object: DropObject, entities: ObjectNameAst[], concurrently?: TokenAst, ifExists?: TokenAst, mode?: DropModeAst } +export type InsertIntoStatementAst = { kind: 'InsertInto', token: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, columns?: IdentifierAst[], values: InsertValueAst[][], onConflict?: OnConflictClauseAst, returning?: SelectClauseAst } +export type SelectStatementAst = { kind: 'Select' } & SelectInnerAst +export type SetStatementAst = { kind: 'Set', token: TokenInfo, scope?: SetModeAst, parameter: IdentifierAst, equal?: SetAssignAst, value: SetValueAst } export type ShowStatementAst = { kind: 'Show', token: TokenInfo, name: IdentifierAst } -export type UpdateStatementAst = { kind: 'Update', token: TokenInfo, only?: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, descendants?: TokenInfo, alias?: AliasAst, columns: UpdateColumnAst[], where?: WhereClauseAst, returning?: SelectClauseAst } +export type UpdateStatementAst = { kind: 'Update', token: TokenInfo, only?: TokenAst, schema?: IdentifierAst, table: IdentifierAst, descendants?: TokenAst, alias?: AliasAst, columns: ColumnUpdateAst[], where?: WhereClauseAst, returning?: SelectClauseAst } -// clauses -export type SchemaRoleAst = { kind: 'Role', name: IdentifierAst } | { kind: 'CurrentRole' | 'CurrentUser' | 'SessionUser', token: TokenInfo } -export type CreateTableModeAst = ({ kind: 'Unlogged', token: TokenInfo }) | ({ kind: 'Temporary', token: TokenInfo, scope?: { kind: 'Local' | 'Global', token: TokenInfo } }) -export type UpdateColumnAst = { column: IdentifierAst, value: ExpressionAst | { kind: 'Default', token: TokenInfo } } -export type SelectStatementInnerAst = SelectStatementMainAst & SelectStatementResultAst -export type SelectStatementMainAst = SelectClauseAst & { from?: FromClauseAst, where?: WhereClauseAst, groupBy?: GroupByClauseAst, having?: HavingClauseAst, window?: WindowClauseAst[] } -export type SelectStatementResultAst = { union?: UnionClauseAst, orderBy?: OrderByClauseAst, limit?: LimitClauseAst, offset?: OffsetClauseAst, fetch?: FetchClauseAst } +// select clauses +export type SelectInnerAst = SelectMainAst & SelectResultAst +export type SelectMainAst = SelectClauseAst & { from?: FromClauseAst, where?: WhereClauseAst, groupBy?: GroupByClauseAst, having?: HavingClauseAst, window?: WindowClauseAst[] } +export type SelectResultAst = { union?: UnionClauseAst, orderBy?: OrderByClauseAst, limit?: LimitClauseAst, offset?: OffsetClauseAst, fetch?: FetchClauseAst } export type SelectClauseAst = { token: TokenInfo, distinct?: { token: TokenInfo, on?: { token: TokenInfo, columns: ExpressionAst[] } }, columns: SelectClauseColumnAst[] } -export type SelectClauseColumnAst = ExpressionAst & { filter?: { token: TokenInfo, where: WhereClauseAst }, over?: { token: TokenInfo } & ({ name: IdentifierAst } | WindowClauseContentAst), alias?: AliasAst } -export type FromClauseAst = FromItemAst & { token: TokenInfo, joins?: FromJoinAst[] } -export type FromItemAst = (FromTableAst | FromQueryAst) & { alias?: AliasAst } -export type FromTableAst = { kind: 'Table', schema?: IdentifierAst, table: IdentifierAst } -export type FromQueryAst = { kind: 'Select', select: SelectStatementInnerAst } -export type FromJoinAst = { kind: JoinKind, token: TokenInfo, from: FromItemAst, on: FromJoinOnAst | FromJoinUsingAst | FromJoinNaturalAst, alias?: AliasAst } -export type FromJoinOnAst = { kind: 'On', token: TokenInfo, predicate: ExpressionAst } -export type FromJoinUsingAst = { kind: 'Using', token: TokenInfo, columns: IdentifierAst[] } -export type FromJoinNaturalAst = { kind: 'Natural', token: TokenInfo } +export type SelectClauseColumnAst = ExpressionAst & { filter?: { token: TokenInfo, where: WhereClauseAst }, over?: TokenAst & ({ name: IdentifierAst } | WindowClauseContentAst), alias?: AliasAst } +export type FromClauseAst = FromClauseItemAst & { token: TokenInfo, joins?: FromClauseJoinAst[] } +export type FromClauseItemAst = (FromClauseTableAst | FromClauseQueryAst) & { alias?: AliasAst } +export type FromClauseTableAst = { kind: 'Table', schema?: IdentifierAst, table: IdentifierAst } +export type FromClauseQueryAst = { kind: 'Select', select: SelectInnerAst } +export type FromClauseJoinAst = { kind: JoinKind, token: TokenInfo, from: FromClauseItemAst, on: JoinOnAst | JoinUsingAst | JoinNaturalAst, alias?: AliasAst } +export type JoinOnAst = { kind: 'On', token: TokenInfo, predicate: ExpressionAst } +export type JoinUsingAst = { kind: 'Using', token: TokenInfo, columns: IdentifierAst[] } +export type JoinNaturalAst = { kind: 'Natural', token: TokenInfo } export type WhereClauseAst = { token: TokenInfo, predicate: ExpressionAst } export type GroupByClauseAst = { token: TokenInfo, mode: { kind: 'All' | 'Distinct', token: TokenInfo }, expressions: ExpressionAst[] } export type HavingClauseAst = { token: TokenInfo, predicate: ExpressionAst } -export type WindowClauseAst = { token: TokenInfo, name: IdentifierAst } & WindowClauseContentAst +export type WindowClauseAst = NameAst & WindowClauseContentAst export type WindowClauseContentAst = { partitionBy?: { token: TokenInfo, columns: ExpressionAst[] }, orderBy?: OrderByClauseAst } -export type UnionClauseAst = { kind: 'Union' | 'Intersect' | 'Except', token: TokenInfo, mode: { kind: 'All' | 'Distinct', token: TokenInfo }, select: SelectStatementInnerAst } // TODO: VALUES also allowed +export type UnionClauseAst = { kind: 'Union' | 'Intersect' | 'Except', token: TokenInfo, mode: { kind: 'All' | 'Distinct', token: TokenInfo }, select: SelectInnerAst } // TODO: VALUES also allowed export type OrderByClauseAst = { token: TokenInfo, expressions: (ExpressionAst & { order?: SortOrderAst, nulls?: SortNullsAst })[] } 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 | AlterColumnAst | DropColumnAst | AddConstraintAst | DropConstraintAst -export type AddColumnAst = { kind: 'AddColumn', token: TokenInfo, ifNotExists?: TokenInfo, column: TableColumnAst } -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[] } -export type TableColumnConstraintAst = TableColumnNullableAst | TableColumnDefaultAst | TableColumnPkAst | TableColumnUniqueAst | TableColumnCheckAst | TableColumnFkAst -export type TableColumnNullableAst = { kind: 'Nullable', value: boolean } & ConstraintCommonAst -export type TableColumnDefaultAst = { kind: 'Default', expression: ExpressionAst } & ConstraintCommonAst -export type TableColumnPkAst = { kind: 'PrimaryKey' } & ConstraintCommonAst -export type TableColumnUniqueAst = { kind: 'Unique' } & ConstraintCommonAst -export type TableColumnCheckAst = { kind: 'Check', predicate: ExpressionAst } & ConstraintCommonAst -export type TableColumnFkAst = { kind: 'ForeignKey', schema?: IdentifierAst, table: IdentifierAst, column?: IdentifierAst, onUpdate?: ForeignKeyActionAst & {token: TokenInfo}, onDelete?: ForeignKeyActionAst & {token: TokenInfo} } & ConstraintCommonAst -export type TableConstraintAst = TablePkAst | TableUniqueAst | TableCheckAst | TableFkAst -export type TablePkAst = { kind: 'PrimaryKey', columns: IdentifierAst[] } & ConstraintCommonAst -export type TableUniqueAst = { kind: 'Unique', columns: IdentifierAst[] } & ConstraintCommonAst -export type TableCheckAst = { kind: 'Check', predicate: ExpressionAst } & ConstraintCommonAst -export type TableFkAst = { kind: 'ForeignKey', columns: IdentifierAst[], ref: { token: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, columns?: IdentifierAst[] }, onUpdate?: ForeignKeyActionAst & {token: TokenInfo}, onDelete?: ForeignKeyActionAst & {token: TokenInfo} } & ConstraintCommonAst -export type ConstraintCommonAst = { token: TokenInfo, constraint?: ConstraintNameAst } -export type ConstraintNameAst = { token: TokenInfo, name: IdentifierAst } -export type ColumnTypeAst = { token: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, args?: IntegerAst[], array?: TokenInfo } + +// other clauses +export type ColumnAlterActionAst = ColumnAlterDefaultAst | ColumnAlterNotNullAst +export type ColumnAlterDefaultAst = { kind: 'Default', action: { kind: 'Set' | 'Drop', token: TokenInfo }, token: TokenInfo, expression?: ExpressionAst } +export type ColumnAlterNotNullAst = { kind: 'NotNull', action: { kind: 'Set' | 'Drop', token: TokenInfo }, token: TokenInfo } +export type ColumnTypeAst = { token: TokenInfo, schema?: IdentifierAst, name: IdentifierAst, args?: IntegerAst[], array?: TokenAst } +export type ColumnUpdateAst = { column: IdentifierAst, value: InsertValueAst } +export type CommentObjectAst = { kind: CommentObject, token: TokenInfo } +export type DropModeAst = { kind: DropMode, token: TokenInfo } +export type ExtensionVersionAst = { token: TokenInfo, number: StringAst | IdentifierAst } export type ForeignKeyActionAst = { action: { kind: ForeignKeyAction, token: TokenInfo }, columns?: IdentifierAst[] } -export type SetValueAst = IdentifierAst | LiteralAst | (IdentifierAst | LiteralAst)[] | { kind: 'Default', token: TokenInfo } +export type FunctionArgumentAst = { mode?: { kind: 'In' | 'Out' | 'InOut' | 'Variadic', token: TokenInfo }, name?: IdentifierAst, type: ColumnTypeAst } +export type FunctionBehaviorAst = { kind: 'Immutable' | 'Stable' | 'Volatile', token: TokenInfo } +export type FunctionDefinitionAst = { token: TokenInfo, value: StringAst } +export type FunctionNullBehaviorAst = { kind: 'Called' | 'ReturnsNull' | 'Strict', token: TokenInfo } +export type FunctionReturnsAst = { kind: 'Type', token: TokenInfo, setOf?: TokenAst, type: ColumnTypeAst } | { kind: 'Table', token: TokenInfo, columns: { name: IdentifierAst, type: ColumnTypeAst }[] } +export type IndexColumnAst = ExpressionAst & { collation?: NameAst, order?: SortOrderAst, nulls?: SortNullsAst } +export type IndexIncludeAst = { token: TokenInfo, columns: IdentifierAst[] } +export type IndexUsingAst = { token: TokenInfo, method: IdentifierAst } +export type InsertValueAst = ExpressionAst | { kind: 'Default', token: TokenInfo } export type OnConflictClauseAst = { token: TokenInfo, target?: OnConflictColumnsAst | OnConflictConstraintAst, action: OnConflictNothingAst | OnConflictUpdateAst } export type OnConflictColumnsAst = { kind: 'Columns', columns: IdentifierAst[], where?: WhereClauseAst } export type OnConflictConstraintAst = { kind: 'Constraint', token: TokenInfo, name: IdentifierAst } export type OnConflictNothingAst = { kind: 'Nothing', token: TokenInfo } -export type OnConflictUpdateAst = { kind: 'Update', columns: UpdateColumnAst[], where?: WhereClauseAst } -export type TransactionModeAst = { kind: 'IsolationLevel', token: TokenInfo, level: { kind: 'Serializable' | 'RepeatableRead' | 'ReadCommitted' | 'ReadUncommitted', token: TokenInfo } } | { kind: 'ReadWrite' | 'ReadOnly', token: TokenInfo } | { kind: 'Deferrable', token: TokenInfo, not?: TokenInfo } +export type OnConflictUpdateAst = { kind: 'Update', columns: ColumnUpdateAst[], where?: WhereClauseAst } +export type SchemaAlterActionAst = { kind: 'Rename', token: TokenInfo, schema: IdentifierAst } | { kind: 'Owner', token: TokenInfo, role: SchemaRoleAst } +export type SchemaAuthorizationAst = { token: TokenInfo, role: SchemaRoleAst } +export type SchemaRoleAst = { kind: 'Role', name: IdentifierAst } | { kind: 'CurrentRole' | 'CurrentUser' | 'SessionUser', token: TokenInfo } export type SequenceModeAst = { kind: 'Unlogged' | 'Temporary', token: TokenInfo } -export type SequenceTypeAst = { token: TokenInfo, type: IdentifierAst } +export type SequenceOwnedByAst = { token: TokenInfo, owner: { kind: 'None', token: TokenInfo } | { kind: 'Column', schema?: IdentifierAst, table: IdentifierAst, column: IdentifierAst } } export type SequenceParamAst = { token: TokenInfo, value: IntegerAst } export type SequenceParamOptAst = { token: TokenInfo, value?: IntegerAst } -export type SequenceOwnedByAst = { token: TokenInfo, owner: { kind: 'None', token: TokenInfo } | { kind: 'Column', schema?: IdentifierAst, table: IdentifierAst, column: IdentifierAst } } -export type FunctionArgumentAst = { mode?: { kind: 'In' | 'Out' | 'InOut' | 'Variadic', token: TokenInfo }, name?: IdentifierAst, type: ColumnTypeAst } -export type FunctionReturnsAst = { kind: 'Type', token: TokenInfo, setOf?: TokenInfo, type: ColumnTypeAst } | { kind: 'Table', token: TokenInfo, columns: { name: IdentifierAst, type: ColumnTypeAst }[] } -export type TriggerTimingAst = { kind: 'Before' | 'After' | 'InsteadOf', token: TokenInfo } -export type TriggerEventAst = { kind: 'Insert' | 'Update' | 'Delete' | 'Truncate', token: TokenInfo, columns?: IdentifierAst } +export type SequenceTypeAst = { token: TokenInfo, type: IdentifierAst } +export type SetAssignAst = { kind: SetAssign, token: TokenInfo } +export type SetModeAst = { kind: SetScope, token: TokenInfo } +export type SetValueAst = IdentifierAst | LiteralAst | (IdentifierAst | LiteralAst)[] | { kind: 'Default', token: TokenInfo } +export type TableAlterActionAst = TableAddColumnAst | TableAddConstraintAst | TableAlterColumnAst | TableDropColumnAst | TableDropConstraintAst +export type TableAddColumnAst = { kind: 'AddColumn', token: TokenInfo, ifNotExists?: TokenAst, column: TableColumnAst } +export type TableAddConstraintAst = { kind: 'AddConstraint', token: TokenInfo, constraint: TableConstraintAst, notValid?: TokenAst } +export type TableAlterColumnAst = { kind: 'AlterColumn', token: TokenInfo, column: IdentifierAst, action: ColumnAlterActionAst } +export type TableDropColumnAst = { kind: 'DropColumn', token: TokenInfo, ifExists?: TokenAst, column: IdentifierAst } +export type TableDropConstraintAst = { kind: 'DropConstraint', token: TokenInfo, ifExists?: TokenAst, constraint: IdentifierAst } +export type TableColumnAst = { name: IdentifierAst, type: ColumnTypeAst, constraints?: TableColumnConstraintAst[] } +export type TableColumnConstraintAst = TableColumnNullableAst | TableColumnDefaultAst | TableColumnPkAst | TableColumnUniqueAst | TableColumnCheckAst | TableColumnFkAst +export type TableColumnNullableAst = { kind: 'Nullable', value: boolean } & TableConstraintCommonAst +export type TableColumnDefaultAst = { kind: 'Default', expression: ExpressionAst } & TableConstraintCommonAst +export type TableColumnPkAst = { kind: 'PrimaryKey' } & TableConstraintCommonAst +export type TableColumnUniqueAst = { kind: 'Unique' } & TableConstraintCommonAst +export type TableColumnCheckAst = { kind: 'Check', predicate: ExpressionAst } & TableConstraintCommonAst +export type TableColumnFkAst = { kind: 'ForeignKey', schema?: IdentifierAst, table: IdentifierAst, column?: IdentifierAst, onUpdate?: ForeignKeyActionAst & {token: TokenInfo}, onDelete?: ForeignKeyActionAst & {token: TokenInfo} } & TableConstraintCommonAst +export type TableConstraintAst = TablePkAst | TableUniqueAst | TableCheckAst | TableFkAst +export type TablePkAst = { kind: 'PrimaryKey', columns: IdentifierAst[] } & TableConstraintCommonAst +export type TableUniqueAst = { kind: 'Unique', columns: IdentifierAst[] } & TableConstraintCommonAst +export type TableCheckAst = { kind: 'Check', predicate: ExpressionAst } & TableConstraintCommonAst +export type TableFkAst = { kind: 'ForeignKey', columns: IdentifierAst[], ref: { token: TokenInfo, schema?: IdentifierAst, table: IdentifierAst, columns?: IdentifierAst[] }, onUpdate?: ForeignKeyActionAst & {token: TokenInfo}, onDelete?: ForeignKeyActionAst & {token: TokenInfo} } & TableConstraintCommonAst +export type TableConstraintCommonAst = { token: TokenInfo, constraint?: NameAst } +export type TableCreateModeAst = ({ kind: 'Unlogged', token: TokenInfo }) | ({ kind: 'Temporary', token: TokenInfo, scope?: { kind: 'Local' | 'Global', token: TokenInfo } }) +export type TransactionChainAst = { token: TokenInfo, no?: TokenAst } +export type TransactionModeAst = { kind: 'IsolationLevel', token: TokenInfo, level: { kind: 'Serializable' | 'RepeatableRead' | 'ReadCommitted' | 'ReadUncommitted', token: TokenInfo } } | { kind: 'ReadWrite' | 'ReadOnly', token: TokenInfo } | { kind: 'Deferrable', token: TokenInfo, not?: TokenAst } +export type TransactionObjectAst = { kind: 'Work' | 'Transaction', token: TokenInfo } export type TriggerDeferrableAst = { kind: 'Deferrable' | 'NotDeferrable', token: TokenInfo, initially?: { kind: 'Immediate' | 'Deferred', token: TokenInfo } } -export type TriggerReferencingAst = { token: TokenInfo, old?: { token: TokenInfo, name: IdentifierAst }, new?: { token: TokenInfo, name: IdentifierAst } } +export type TriggerEventAst = { kind: 'Insert' | 'Update' | 'Delete' | 'Truncate', token: TokenInfo, columns?: IdentifierAst } +export type TriggerExecuteAst = { token: TokenInfo, schema?: IdentifierAst, function: IdentifierAst, arguments: ExpressionAst[] } +export type TriggerFromAst = { token: TokenInfo, schema?: IdentifierAst, table: IdentifierAst } +export type TriggerReferencingAst = { token: TokenInfo, old?: NameAst, new?: NameAst } +export type TriggerTargetAst = { kind: 'Row' | 'Statement', token: TokenInfo } +export type TriggerTimingAst = { kind: 'Before' | 'After' | 'InsteadOf', token: TokenInfo } +export type TypeBaseAttrAst = { name: IdentifierAst, value: ExpressionAst } +export type TypeColumnAst = { name: IdentifierAst, type: ColumnTypeAst, collation?: NameAst } +export type TypeEnumAst = { token: TokenInfo, values: StringAst[] } +export type TypeStructAst = { token: TokenInfo, attrs: TypeColumnAst[] } +export type ViewMaterializedDataAst = { token: TokenInfo, no?: TokenAst } // basic parts export type AliasAst = { token?: TokenInfo, name: IdentifierAst } +export type NameAst = { token: TokenInfo, name: IdentifierAst } +export type FilterAst = { token: TokenInfo, condition: ExpressionAst } export type ObjectNameAst = { schema?: IdentifierAst, name: IdentifierAst } export type ExpressionAst = (LiteralAst | ParameterAst | ColumnAst | WildcardAst | FunctionAst | GroupAst | OperationAst | OperationLeftAst | OperationRightAst | ArrayAst | ListAst) & { cast?: { token: TokenInfo, type: ColumnTypeAst } } export type LiteralAst = StringAst | IntegerAst | DecimalAst | BooleanAst | NullAst export type ColumnAst = { kind: 'Column', schema?: IdentifierAst, table?: IdentifierAst, column: IdentifierAst, json?: ColumnJsonAst[] } export type ColumnJsonAst = { kind: JsonOp, token: TokenInfo, field: StringAst | ParameterAst } -export type FunctionAst = { kind: 'Function', schema?: IdentifierAst, function: IdentifierAst, distinct?: { token: TokenInfo }, parameters: ExpressionAst[] } +export type FunctionAst = { kind: 'Function', schema?: IdentifierAst, function: IdentifierAst, distinct?: TokenAst, parameters: ExpressionAst[] } export type GroupAst = { kind: 'Group', expression: ExpressionAst } export type WildcardAst = { kind: 'Wildcard', token: TokenInfo, schema?: IdentifierAst, table?: IdentifierAst } export type OperationAst = { kind: 'Operation', left: ExpressionAst, op: OperatorAst, right: ExpressionAst } @@ -146,6 +171,7 @@ export type IntegerAst = { kind: 'Integer', token: TokenInfo, value: number } export type BooleanAst = { kind: 'Boolean', token: TokenInfo, value: boolean } export type NullAst = { kind: 'Null', token: TokenInfo } export type CommentAst = { kind: CommentKind, token: TokenInfo, value: string } // special case +export type TokenAst = { token: TokenInfo } // enums export type Operator = '+' | '-' | '*' | '/' | '%' | '^' | '&' | '|' | '#' | '<<' | '>>' | '=' | '<' | '>' | '<=' | '>=' | '<>' | '!=' | '||' | '~' | '~*' | '!~' | '!~*' | 'Is' | 'IsNot' | 'Like' | 'NotLike' | 'In' | 'NotIn' | 'DistinctFrom' | 'NotDistinctFrom' | 'Or' | 'And' diff --git a/libs/parser-sql/src/postgresBuilder.test.ts b/libs/parser-sql/src/postgresBuilder.test.ts index 9f807afb8..596352c54 100644 --- a/libs/parser-sql/src/postgresBuilder.test.ts +++ b/libs/parser-sql/src/postgresBuilder.test.ts @@ -2,7 +2,7 @@ import * as fs from "fs"; import {describe, expect, test} from "@jest/globals"; import {removeFieldsDeep} from "@azimutt/utils"; import {Database, Entity, parseJsonDatabase, ParserError} from "@azimutt/models"; -import {SelectStatementInnerAst} from "./postgresAst"; +import {SelectInnerAst} from "./postgresAst"; import {parsePostgresAst} from "./postgresParser"; import {buildPostgresDatabase, SelectEntities, selectEntities} from "./postgresBuilder"; @@ -208,5 +208,5 @@ function parse(sql: string): {db: Database, errors: ParserError[]} { } function extract(sql: string, entities: Entity[]): SelectEntities { - return removeFieldsDeep(selectEntities(parsePostgresAst(sql).result?.statements?.[0] as SelectStatementInnerAst, entities), ['token']) + return removeFieldsDeep(selectEntities(parsePostgresAst(sql).result?.statements?.[0] as SelectInnerAst, entities), ['token']) } diff --git a/libs/parser-sql/src/postgresBuilder.ts b/libs/parser-sql/src/postgresBuilder.ts index 52c74a41b..80fff03ff 100644 --- a/libs/parser-sql/src/postgresBuilder.ts +++ b/libs/parser-sql/src/postgresBuilder.ts @@ -38,7 +38,7 @@ import { CreateViewStatementAst, ExpressionAst, FromClauseAst, - FromItemAst, + FromClauseItemAst, FunctionAst, IdentifierAst, Operator, @@ -46,7 +46,7 @@ import { OperatorRight, PostgresAst, SelectClauseColumnAst, - SelectStatementInnerAst, + SelectInnerAst, StatementAst, TableColumnAst, TokenInfo @@ -223,7 +223,7 @@ function createMaterializedView(index: number, stmt: CreateMaterializedViewState }) } -function buildViewAttrs(index: number, query: SelectStatementInnerAst, columns: IdentifierAst[] | undefined, entities: Entity[]): Attribute[] { +function buildViewAttrs(index: number, query: SelectInnerAst, columns: IdentifierAst[] | undefined, entities: Entity[]): Attribute[] { const attrs = selectEntities(query, entities).columns.map(c => removeUndefined({ name: c.name, type: c.type || 'unknown', @@ -385,7 +385,7 @@ function commentOn(index: number, stmt: CommentOnStatementAst, db: Database): vo } } -function selectInnerToString(s: SelectStatementInnerAst): string { +function selectInnerToString(s: SelectInnerAst): string { const select = 'SELECT ' + s.columns.map(c => expressionToString(c) + (c.alias ? ' ' + aliasToString(c.alias) : '')).join(', ') const from = s.from ? fromToString(s.from) : '' const where = s.where ? ' WHERE ' + expressionToString(s.where.predicate) : '' @@ -505,7 +505,7 @@ export type SelectSourceTable = { kind: 'Table', schema?: string, table: string, export type SelectSourceSelect = { kind: 'Select' } & SelectEntities // TODO: also extract entities in clauses such as WHERE, HAVING... (know all use involved tables & columns, wherever they are used) -export function selectEntities(s: SelectStatementInnerAst, entities: Entity[]): SelectEntities { +export function selectEntities(s: SelectInnerAst, entities: Entity[]): SelectEntities { const sources = s.from ? selectTables(s.from, entities) : [] const columns: SelectColumn[] = s.columns.flatMap((c, i) => selectColumn(c, i, sources)) return {columns, sources} @@ -514,7 +514,7 @@ function selectTables(f: FromClauseAst, entities: Entity[]): SelectSource[] { const joins = f.joins?.map(j => fromTables(j.from, entities)) || [] return [fromTables(f, entities), ...joins] } -function fromTables(i: FromItemAst, entities: Entity[]): SelectSource { +function fromTables(i: FromClauseItemAst, entities: Entity[]): SelectSource { if (i.kind === 'Table') { const entity = findEntity(entities, i.table.value, i.schema?.value) if (entity) { diff --git a/libs/parser-sql/src/postgresParser.test.ts b/libs/parser-sql/src/postgresParser.test.ts index 39e50d4c5..5c6c25456 100644 --- a/libs/parser-sql/src/postgresParser.test.ts +++ b/libs/parser-sql/src/postgresParser.test.ts @@ -82,26 +82,26 @@ describe('postgresParser', () => { // 'https://raw.githubusercontent.com/gitlabhq/gitlabhq/refs/heads/master/db/init_structure.sql', // fail#209: `SECURITY DEFINER` in `CREATE FUNCTION` // 'https://raw.githubusercontent.com/gitlabhq/gitlabhq/refs/heads/master/db/structure.sql', // fail#195: `STABLE COST 1 PARALLEL SAFE` in `CREATE FUNCTION` // 'https://raw.githubusercontent.com/mattermost/mattermost/refs/heads/master/server/scripts/mattermost-postgresql-6.0.0.sql', // fail#36: `ALTER TABLE public.audits OWNER TO mmuser;` TODO - // 'https://raw.githubusercontent.com/Rotabot-io/rotabot/refs/heads/main/assets/structure.sql', // fail#57: `ALTER FUNCTION public.generate_uid(size integer) OWNER TO rotabot;` TODO - // 'https://raw.githubusercontent.com/bocoup/devstats/refs/heads/master/structure.sql', // fail#57: `ALTER FUNCTION current_state.label_prefix(some_label text) OWNER TO devstats_team;` TODO // 'https://raw.githubusercontent.com/dokuwiki/dokuwiki/refs/heads/master/lib/plugins/authpdo/_test/pgsql/django.sql', // fail#44: `ALTER TABLE auth_group OWNER TO postgres;` TODO // 'https://raw.githubusercontent.com/Unleash/unleash/refs/heads/main/website/docs/migrations/unleash_dump.sql', // fail#38: `ALTER TABLE public.addons OWNER TO unleash_user;` TODO + // 'https://raw.githubusercontent.com/Rotabot-io/rotabot/refs/heads/main/assets/structure.sql', // fail#57: `ALTER FUNCTION public.generate_uid(size integer) OWNER TO rotabot;` TODO + // 'https://raw.githubusercontent.com/bocoup/devstats/refs/heads/master/structure.sql', // fail#57: `ALTER FUNCTION current_state.label_prefix(some_label text) OWNER TO devstats_team;` TODO + // 'https://raw.githubusercontent.com/yazilimcilarinmolayeri/pixels-clone/refs/heads/master/Structure.sql', // fail#68: column `GENERATED ALWAYS AS IDENTITY` TODO // 'https://raw.githubusercontent.com/metabase/metabase/refs/heads/master/resources/migrations/initialization/metabase_postgres.sql', // fail#29: `ALTER TABLE public.activity ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY` + // 'https://raw.githubusercontent.com/knadh/listmonk/refs/heads/master/schema.sql', // fail#58: `INTEGER REFERENCES subscribers(id) ON DELETE CASCADE ON UPDATE CASCADE` + // 'https://raw.githubusercontent.com/drenther/Empirical-Core/refs/heads/develop/db/structure.sql', // fail#2894: `CREATE INDEX email_idx ON users USING gin (email gin_trgm_ops);` + // 'https://raw.githubusercontent.com/TechnoDann/PPC-board-2.0/refs/heads/master/db/structure.sql', // fail#350: `CREATE INDEX index_posts_on_ancestry ON public.posts USING btree (ancestry text_pattern_ops NULLS FIRST);` + // 'https://raw.githubusercontent.com/goksan/statusnook/refs/heads/main/schema.sql', // fail#65: `destination text not null collate nocase` + // 'https://raw.githubusercontent.com/Style12341/UTN-gestor-aulas-backend/refs/heads/main/db/structure.sql', // fail#25: `CREATE TYPE public.timerange AS RANGE` // 'https://raw.githubusercontent.com/inaturalist/inaturalist/refs/heads/main/db/structure.sql', // fail#44: `CREATE FUNCTION` with unnamed parameter // 'https://raw.githubusercontent.com/cardstack/cardstack/refs/heads/main/packages/hub/config/structure.sql', // fail#52: `ALTER TYPE` & 1986: `COPY` - // 'https://raw.githubusercontent.com/drenther/Empirical-Core/refs/heads/develop/db/structure.sql', // fail#2894: `CREATE INDEX email_idx ON users USING gin (email gin_trgm_ops);` // 'https://raw.githubusercontent.com/CLOSER-Cohorts/archivist/refs/heads/develop/db/structure.sql', // fail#849: parenthesis in FROM clause // 'https://raw.githubusercontent.com/PecanProject/bety/refs/heads/master/db/structure.sql', // fail#274: LexingError: unexpected character: ->\\<- - // 'https://raw.githubusercontent.com/dhbtk/achabus/refs/heads/master/db/structure.sql', // fail#293: column type: `geography(Point,4326)` // 'https://raw.githubusercontent.com/recursecenter/community/refs/heads/master/db/structure.sql', // fail#299: JOIN (`SELECT * FROM users, events;`) - // 'https://raw.githubusercontent.com/yazilimcilarinmolayeri/pixels-clone/refs/heads/master/Structure.sql', // fail#68: column `GENERATED ALWAYS AS IDENTITY` TODO + // 'https://raw.githubusercontent.com/dhbtk/achabus/refs/heads/master/db/structure.sql', // fail#293: column type: `geography(Point,4326)` // 'https://raw.githubusercontent.com/ppawel/openstreetmap-watch-list/refs/heads/master/db/structure.sql', // fail#92: `CREATE TYPE change AS (geom geometry(Geometry,4326))` // 'https://raw.githubusercontent.com/OPG-813/electronic-queue-server/refs/heads/master/src/db/structure.sql', // fail#110: `DEFAULT CURRENT_DATE AT TIME ZONE( SYSTEM_TIMEZONE() )` - // 'https://raw.githubusercontent.com/TechnoDann/PPC-board-2.0/refs/heads/master/db/structure.sql', // fail#350: `CREATE INDEX index_posts_on_ancestry ON public.posts USING btree (ancestry text_pattern_ops NULLS FIRST);` - // 'https://raw.githubusercontent.com/Style12341/UTN-gestor-aulas-backend/refs/heads/main/db/structure.sql', // fail#25: `CREATE TYPE public.timerange AS RANGE` - // 'https://raw.githubusercontent.com/knadh/listmonk/refs/heads/master/schema.sql', // fail#58: `INTEGER REFERENCES subscribers(id) ON DELETE CASCADE ON UPDATE CASCADE` // 'https://raw.githubusercontent.com/yourselfhosted/slash/refs/heads/main/store/migration/postgres/prod/LATEST.sql', // fail#4: `EXTRACT(EPOCH FROM NOW())` - // 'https://raw.githubusercontent.com/goksan/statusnook/refs/heads/main/schema.sql', // fail#65: `destination text not null collate nocase` ] await Promise.all(structures.map(async url => { const sql = await fetch(url).then(res => res.text()) @@ -139,7 +139,7 @@ describe('postgresParser', () => { test('complex', () => { expect(parsePostgresAst('ALTER SEQUENCE IF EXISTS public.users_id_seq AS integer START WITH 1 INCREMENT BY 1 MINVALUE 1 NO MAXVALUE CACHE 1 OWNED BY users.id;')).toEqual({result: {statements: [{ ...stmt('AlterSequence', 0, 13, 132), - ifExists: token(15, 23), + ifExists: {token: token(15, 23)}, schema: identifier('public', 25), name: identifier('users_id_seq', 32), as: {token: token(45, 46), type: identifier('integer', 48)}, @@ -156,8 +156,8 @@ describe('postgresParser', () => { test('full', () => { expect(parsePostgresAst('ALTER TABLE IF EXISTS ONLY public.users ADD CONSTRAINT users_pk PRIMARY KEY (id);')).toEqual({result: {statements: [{ ...stmt('AlterTable', 0, 10, 80), - ifExists: token(12, 20), - only: token(22, 25), + ifExists: {token: token(12, 20)}, + only: {token: token(22, 25)}, schema: identifier('public', 27), table: identifier('users', 34), actions: [{...kind('AddConstraint', 40, 42), constraint: {constraint: {token: token(44, 53), name: identifier('users_pk', 55)}, ...kind('PrimaryKey', 64, 74), columns: [identifier('id', 77)]}}], @@ -180,7 +180,7 @@ describe('postgresParser', () => { test('alter column default', () => { expect(parsePostgresAst("ALTER TABLE ONLY public.posts ALTER COLUMN id SET DEFAULT nextval('public.posts_id_seq'::regclass);")).toEqual({result: {statements: [{ ...stmt('AlterTable', 0, 10, 98), - only: token(12, 15), + only: {token: token(12, 15)}, schema: identifier('public', 17), table: identifier('posts', 24), actions: [{ @@ -195,7 +195,7 @@ describe('postgresParser', () => { }]}}) expect(parsePostgresAst("ALTER TABLE ONLY public.posts ALTER COLUMN id DROP DEFAULT;")).toEqual({result: {statements: [{ ...stmt('AlterTable', 0, 10, 58), - only: token(12, 15), + only: {token: token(12, 15)}, schema: identifier('public', 17), table: identifier('posts', 24), actions: [{...kind('AlterColumn', 30, 41), column: identifier('id', 43), action: {...kind('Default', 51, 57), action: kind('Drop', 46)}}] @@ -204,7 +204,7 @@ describe('postgresParser', () => { test('alter column not null', () => { expect(parsePostgresAst("ALTER TABLE ONLY public.posts ALTER COLUMN id DROP NOT NULL;")).toEqual({result: {statements: [{ ...stmt('AlterTable', 0, 10, 59), - only: token(12, 15), + only: {token: token(12, 15)}, schema: identifier('public', 17), table: identifier('posts', 24), actions: [{...kind('AlterColumn', 30, 41), column: identifier('id', 43), action: {...kind('NotNull', 51, 58), action: kind('Drop', 46)}}] @@ -214,7 +214,7 @@ describe('postgresParser', () => { expect(parsePostgresAst('ALTER TABLE users ADD PRIMARY KEY (id) NOT VALID;')).toEqual({result: {statements: [{ ...stmt('AlterTable', 0, 10, 48), table: identifier('users', 12), - actions: [{...kind('AddConstraint', 18, 20), constraint: {...kind('PrimaryKey', 22, 32), columns: [identifier('id', 35)]}, notValid: token(39, 47)}], + actions: [{...kind('AddConstraint', 18, 20), constraint: {...kind('PrimaryKey', 22, 32), columns: [identifier('id', 35)]}, notValid: {token: token(39, 47)}}], }]}}) }) test('drop primaryKey', () => { @@ -236,7 +236,7 @@ describe('postgresParser', () => { modes: [ {...kind('IsolationLevel', 18, 32), level: kind('ReadCommitted', 34, 47)}, kind('ReadOnly', 50, 58), - {not: token(61, 63), ...kind('Deferrable', 65, 74)}, + {not: {token: token(61, 63)}, ...kind('Deferrable', 65, 74)}, ] }]}}) }) @@ -288,7 +288,7 @@ describe('postgresParser', () => { expect(parsePostgresAst("COMMIT WORK AND NO CHAIN;")).toEqual({result: {statements: [{ ...stmt('Commit', 0, 5, 24), object: kind('Work', 7), - chain: {token: token(12, 23), no: token(16, 17)}, + chain: {token: token(12, 23), no: {token: token(16, 17)}}, }]}}) }) }) @@ -302,12 +302,12 @@ describe('postgresParser', () => { test('full', () => { expect(parsePostgresAst("CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public VERSION '1.0' CASCADE;")).toEqual({result: {statements: [{ ...stmt('CreateExtension', 0, 15, 78), - ifNotExists: token(17, 29), + ifNotExists: {token: token(17, 29)}, name: identifier('citext', 31), - with: token(38, 41), + with: {token: token(38, 41)}, schema: {token: token(43, 48), name: identifier('public', 50)}, version: {token: token(57, 63), number: string('1.0', 65)}, - cascade: token(71, 77), + cascade: {token: token(71, 77)}, }]}}) }) }) @@ -339,7 +339,7 @@ describe('postgresParser', () => { language: {token: token(58, 65), name: identifier('SQL', 67)}, behavior: kind('Immutable', 71), nullBehavior: kind('ReturnsNull', 81, 106), - return: {token: token(108, 113), expression: operation(column('a', 115), op('+', 117), column('b', 119))} + return: {token: token(108, 113), condition: operation(column('a', 115), op('+', 117), column('b', 119))} }]}}) }) test('using plSQL', () => { @@ -385,11 +385,11 @@ describe('postgresParser', () => { ' INCLUDE (email);' )).toEqual({result: {statements: [{ ...stmt('CreateIndex', 0, 18, 163), - unique: token(7, 12), - concurrently: token(20, 31), - ifNotExists: token(33, 45), + unique: {token: token(7, 12)}, + concurrently: {token: token(20, 31)}, + ifNotExists: {token: token(33, 45)}, index: identifier('users_name_idx', 47), - only: token(65, 68), + only: {token: token(65, 68)}, schema: identifier('public', 70), table: identifier('users', 77), using: {token: token(83, 87), method: identifier('btree', 89)}, @@ -429,7 +429,7 @@ describe('postgresParser', () => { test('complex', () => { expect(parsePostgresAst('CREATE SCHEMA IF NOT EXISTS cms AUTHORIZATION CURRENT_USER;')).toEqual({result: {statements: [{ ...stmt('CreateSchema', 0, 12, 58), - ifNotExists: token(14, 26), + ifNotExists: {token: token(14, 26)}, schema: identifier('cms', 28), authorization: {token: token(32, 44), role: kind('CurrentUser', 46, 57)}, }]}}) @@ -446,7 +446,7 @@ describe('postgresParser', () => { expect(parsePostgresAst('CREATE UNLOGGED SEQUENCE IF NOT EXISTS public.users_id_seq AS integer START WITH 1 INCREMENT BY 1 MINVALUE 1 NO MAXVALUE CACHE 1 OWNED BY users.id;')).toEqual({result: {statements: [{ ...stmt('CreateSequence', 0, 23, 146), mode: kind('Unlogged', 7), - ifNotExists: token(25, 37), + ifNotExists: {token: token(25, 37)}, schema: identifier('public', 39), name: identifier('users_id_seq', 46), as: {token: token(59, 60), type: identifier('integer', 62)}, @@ -504,7 +504,7 @@ describe('postgresParser', () => { 'WHEN (OLD.* IS DISTINCT FROM NEW.*) ' + 'EXECUTE PROCEDURE public.check_account_delete(\'check\', 1);')).toEqual({result: {statements: [{ ...stmt('CreateTrigger', 0, 35, 332), - replace: token(7, 16), + replace: {token: token(7, 16)}, constraint: {token: token(18, 27)}, name: identifier('check_delete', 37), timing: kind('InsteadOf', 50, 59), @@ -575,9 +575,9 @@ describe('postgresParser', () => { test('full', () => { expect(parsePostgresAst("CREATE OR REPLACE TEMP RECURSIVE VIEW admins (id, name) AS SELECT * FROM users WHERE role = 'admin';")).toEqual({result: {statements: [{ ...stmt('CreateView', 0, 36, 99), - replace: token(7, 16), - temporary: token(18, 21), - recursive: token(23, 31), + replace: {token: token(7, 16)}, + temporary: {token: token(18, 21)}, + recursive: {token: token(23, 31)}, name: identifier('admins', 38), columns: [identifier('id', 46), identifier('name', 50)], query: { @@ -599,10 +599,10 @@ describe('postgresParser', () => { test('complex', () => { expect(parsePostgresAst("DELETE FROM ONLY public.tasks * t WHERE t.status = 'DONE' RETURNING *;")).toEqual({result: {statements: [{ ...stmt('Delete', 0, 10, 69), - only: token(12, 15), + only: {token: token(12, 15)}, schema: identifier('public', 17), table: identifier('tasks', 24), - descendants: token(30, 30), + descendants: {token: token(30, 30)}, alias: alias('t', 32), where: {token: token(34, 38), predicate: operation(column('status', 40, 't'), op('=', 49), string('DONE', 51))}, returning: {token: token(58, 66), columns: [wildcard(68)]} @@ -633,8 +633,8 @@ describe('postgresParser', () => { expect(parsePostgresAst('DROP INDEX CONCURRENTLY IF EXISTS users_idx, posts_idx CASCADE;')).toEqual({result: {statements: [{ ...stmt('Drop', 0, 9, 62), object: 'Index', - concurrently: token(11, 22), - ifExists: token(24, 32), + concurrently: {token: token(11, 22)}, + ifExists: {token: token(24, 32)}, entities: [{name: identifier('users_idx', 34)}, {name: identifier('posts_idx', 45)}], mode: kind('Cascade', 55), }]}}) @@ -1183,7 +1183,7 @@ describe('postgresParser', () => { expect(parseRule(p => p.columnTypeRule(), 'NUMERIC(2, -3)')).toEqual({result: {name: identifier('NUMERIC(2, -3)', 0), args: [integer(2, 8), integer(-3, 11)], token: token(0, 13)}}) }) test('array', () => { - expect(parseRule(p => p.columnTypeRule(), 'int[]')).toEqual({result: {name: identifier('int[]', 0), array: token(3, 4), token: token(0, 4)}}) + expect(parseRule(p => p.columnTypeRule(), 'int[]')).toEqual({result: {name: identifier('int[]', 0), array: {token: token(3, 4)}, token: token(0, 4)}}) }) test('with time zone', () => { expect(parseRule(p => p.columnTypeRule(), 'timestamp with time zone')).toEqual({result: {name: identifier('timestamp with time zone', 0), token: token(0, 23)}}) diff --git a/libs/parser-sql/src/postgresParser.ts b/libs/parser-sql/src/postgresParser.ts index 07844eda1..5e5d37b74 100644 --- a/libs/parser-sql/src/postgresParser.ts +++ b/libs/parser-sql/src/postgresParser.ts @@ -13,16 +13,15 @@ import { AliasAst, AlterSchemaStatementAst, AlterSequenceStatementAst, - AlterTableActionAst, AlterTableStatementAst, BeginStatementAst, BooleanAst, ColumnJsonAst, ColumnTypeAst, + ColumnUpdateAst, CommentAst, CommentOnStatementAst, CommitStatementAst, - ConstraintNameAst, CreateExtensionStatementAst, CreateFunctionStatementAst, CreateIndexStatementAst, @@ -40,10 +39,10 @@ import { FetchClauseAst, ForeignKeyActionAst, FromClauseAst, - FromItemAst, - FromJoinAst, - FromQueryAst, - FromTableAst, + FromClauseItemAst, + FromClauseJoinAst, + FromClauseQueryAst, + FromClauseTableAst, FunctionArgumentAst, FunctionReturnsAst, GroupAst, @@ -57,6 +56,7 @@ import { LimitClauseAst, ListAst, LiteralAst, + NameAst, NullAst, ObjectNameAst, OffsetClauseAst, @@ -71,10 +71,10 @@ import { SchemaRoleAst, SelectClauseAst, SelectClauseColumnAst, + SelectInnerAst, + SelectMainAst, + SelectResultAst, SelectStatementAst, - SelectStatementInnerAst, - SelectStatementMainAst, - SelectStatementResultAst, SequenceOwnedByAst, SequenceParamAst, SequenceParamOptAst, @@ -86,6 +86,7 @@ import { StatementAst, StatementsAst, StringAst, + TableAlterActionAst, TableColumnAst, TableColumnCheckAst, TableColumnConstraintAst, @@ -106,7 +107,6 @@ import { TriggerReferencingAst, TypeColumnAst, UnionClauseAst, - UpdateColumnAst, UpdateStatementAst, WhereClauseAst, WindowClauseAst, @@ -354,15 +354,15 @@ class PostgresParser extends EmbeddedActionsParser { commentOnStatementRule: () => CommentOnStatementAst commitStatementRule: () => CommitStatementAst createExtensionStatementRule: (create: IToken) => CreateExtensionStatementAst - createFunctionStatementRule: (create: IToken, replace?: TokenInfo) => CreateFunctionStatementAst + createFunctionStatementRule: (create: IToken, replace?: { token: TokenInfo }) => CreateFunctionStatementAst createIndexStatementRule: (create: IToken) => CreateIndexStatementAst createMaterializedViewStatementRule: (create: IToken) => CreateMaterializedViewStatementAst createSchemaStatementRule: (create: IToken) => CreateSchemaStatementAst createSequenceStatementRule: (create: IToken) => CreateSequenceStatementAst createTableStatementRule: (create: IToken) => CreateTableStatementAst - createTriggerStatementRule: (create: IToken, replace?: TokenInfo) => CreateTriggerStatementAst + createTriggerStatementRule: (create: IToken, replace?: { token: TokenInfo }) => CreateTriggerStatementAst createTypeStatementRule: (create: IToken) => CreateTypeStatementAst - createViewStatementRule: (create: IToken, replace?: TokenInfo) => CreateViewStatementAst + createViewStatementRule: (create: IToken, replace?: { token: TokenInfo }) => CreateViewStatementAst deleteStatementRule: () => DeleteStatementAst dropStatementRule: () => DropStatementAst insertIntoStatementRule: () => InsertIntoStatementAst @@ -415,7 +415,7 @@ class PostgresParser extends EmbeddedActionsParser { {ALT: () => $.SUBRULE($.commitStatementRule)}, {ALT: () => { const create = $.CONSUME(Create) - const replace = $.OPTION(() => tokenInfo2($.CONSUME(Or), $.CONSUME(Replace))) + const replace = $.OPTION(() => ({token: tokenInfo2($.CONSUME(Or), $.CONSUME(Replace))})) return $.OR2([ {ALT: () => $.SUBRULE($.createExtensionStatementRule, {ARGS: [create]})}, {ALT: () => $.SUBRULE($.createFunctionStatementRule, {ARGS: [create, replace]})}, @@ -475,9 +475,9 @@ class PostgresParser extends EmbeddedActionsParser { const start = $.CONSUME(Alter) const token = tokenInfo2(start, $.CONSUME(Table)) const ifExists = $.SUBRULE(ifExistsRule) - const only = $.OPTION(() => tokenInfo($.CONSUME(Only))) + const only = $.OPTION(() => ({token: tokenInfo($.CONSUME(Only))})) const object = $.SUBRULE($.objectNameRule) - const actions: AlterTableActionAst[] = [] + const actions: TableAlterActionAst[] = [] $.MANY_SEP({SEP: Comma, DEF: () => actions.push($.OR([ {ALT: () => removeUndefined({kind: 'AddColumn' as const, token: tokenInfo2($.CONSUME(Add), $.OPTION2(() => $.CONSUME(Column))), ifNotExists: $.SUBRULE(ifNotExistsRule), column: $.SUBRULE($.tableColumnRule)})}, {ALT: () => removeUndefined({kind: 'DropColumn' as const, token: tokenInfo2($.CONSUME(Drop), $.OPTION3(() => $.CONSUME2(Column))), ifExists: $.SUBRULE2(ifExistsRule), column: $.SUBRULE($.identifierRule)})}, @@ -485,7 +485,7 @@ class PostgresParser extends EmbeddedActionsParser { {ALT: () => removeUndefined({kind: 'Default' as const, action: $.SUBRULE(constraintActionRule), token: tokenInfo($.CONSUME(Default)), expression: $.OPTION5(() => $.SUBRULE($.expressionRule))})}, {ALT: () => ({kind: 'NotNull' as const, action: $.SUBRULE2(constraintActionRule), token: tokenInfo2($.CONSUME(Not), $.CONSUME(Null))})}, ])})}, - {ALT: () => removeUndefined({kind: 'AddConstraint' as const, token: tokenInfo($.CONSUME2(Add)), constraint: $.SUBRULE($.tableConstraintRule), notValid: $.OPTION6(() => tokenInfo2($.CONSUME2(Not), $.CONSUME(Valid)))})}, + {ALT: () => removeUndefined({kind: 'AddConstraint' as const, token: tokenInfo($.CONSUME2(Add)), constraint: $.SUBRULE($.tableConstraintRule), notValid: $.OPTION6(() => ({token: tokenInfo2($.CONSUME2(Not), $.CONSUME(Valid))}))})}, {ALT: () => removeUndefined({kind: 'DropConstraint' as const, token: tokenInfo2($.CONSUME2(Drop), $.CONSUME(Constraint)), ifExists: $.SUBRULE3(ifExistsRule), constraint: $.SUBRULE3($.identifierRule)})}, ]))}) const end = $.CONSUME(Semicolon) @@ -514,7 +514,7 @@ class PostgresParser extends EmbeddedActionsParser { ])})}, {ALT: () => ({kind: 'ReadOnly' as const, token: tokenInfo($.CONSUME(ReadOnly))})}, {ALT: () => ({kind: 'ReadWrite' as const, token: tokenInfo($.CONSUME(ReadWrite))})}, - {ALT: () => ({not: $.OPTION2(() => tokenInfo($.CONSUME(Not))), kind: 'Deferrable' as const, token: tokenInfo($.CONSUME(Deferrable))})} + {ALT: () => ({not: $.OPTION2(() => ({token: tokenInfo($.CONSUME(Not))})), kind: 'Deferrable' as const, token: tokenInfo($.CONSUME(Deferrable))})} ]))}) const end = $.CONSUME(Semicolon) return removeEmpty({kind: 'Begin' as const, meta: tokenInfo2(start, end), token, object, modes}) @@ -576,7 +576,7 @@ class PostgresParser extends EmbeddedActionsParser { ])) const chain = $.OPTION2(() => { const and = $.CONSUME(And) - const no = $.OPTION3(() => tokenInfo($.CONSUME(No))) + const no = $.OPTION3(() => ({token: tokenInfo($.CONSUME(No))})) const chain = $.CONSUME(Chain) return {token: tokenInfo2(and, chain), no} }) @@ -589,15 +589,15 @@ class PostgresParser extends EmbeddedActionsParser { const token = tokenInfo2(create, $.CONSUME(Extension)) const ifNotExists = $.SUBRULE(ifNotExistsRule) const name = $.SUBRULE($.identifierRule) - const withh = $.OPTION(() => tokenInfo($.CONSUME(With))) + const with_ = $.OPTION(() => ({token: tokenInfo($.CONSUME(With))})) const schema = $.OPTION2(() => ({token: tokenInfo($.CONSUME(Schema)), name: $.SUBRULE2($.identifierRule)})) const version = $.OPTION3(() => ({token: tokenInfo($.CONSUME(Version)), number: $.OR([{ALT: () => $.SUBRULE($.stringRule)}, {ALT: () => $.SUBRULE3($.identifierRule)}])})) - const cascade = $.OPTION4(() => tokenInfo($.CONSUME(Cascade))) + const cascade = $.OPTION4(() => ({token: tokenInfo($.CONSUME(Cascade))})) const end = $.CONSUME(Semicolon) - return removeUndefined({kind: 'CreateExtension' as const, meta: tokenInfo2(create, end), token, ifNotExists, name, with: withh, schema, version, cascade}) + return removeUndefined({kind: 'CreateExtension' as const, meta: tokenInfo2(create, end), token, ifNotExists, name, with: with_, schema, version, cascade}) }) - this.createFunctionStatementRule = $.RULE<(create: IToken) => CreateFunctionStatementAst>('createFunctionStatementRule', (create: IToken, replace?: TokenInfo) => { + this.createFunctionStatementRule = $.RULE<(create: IToken, replace?: { token: TokenInfo }) => CreateFunctionStatementAst>('createFunctionStatementRule', (create: IToken, replace?: { token: TokenInfo }) => { // https://www.postgresql.org/docs/current/sql-createfunction.html const token = tokenInfo2(create, $.CONSUME(Function)) const object = $.SUBRULE($.objectNameRule) @@ -617,7 +617,7 @@ class PostgresParser extends EmbeddedActionsParser { {ALT: () => ({kind: 'ReturnsNull' as const, token: tokenInfoN([$.CONSUME(Returns), $.CONSUME2(Null), $.CONSUME2(On), $.CONSUME3(Null), $.CONSUME2(Input)])})}, {ALT: () => ({kind: 'Strict' as const, token: tokenInfo($.CONSUME(Strict))})}, ])}, - {ALT: () => statement.return = {token: tokenInfo($.CONSUME(Return)), expression: $.SUBRULE(this.expressionRule)}}, + {ALT: () => statement.return = {token: tokenInfo($.CONSUME(Return)), condition: $.SUBRULE(this.expressionRule)}}, ])}) const end = $.CONSUME(Semicolon) return removeUndefined({kind: 'CreateFunction' as const, meta: tokenInfo2(create, end), token, replace, ...object, args, ...statement}) @@ -642,7 +642,7 @@ class PostgresParser extends EmbeddedActionsParser { const functionReturnsRule = $.RULE<() => FunctionReturnsAst>('functionReturnsRule', () => { const ret = $.CONSUME(Returns) return $.OR([ - {ALT: () => removeUndefined({kind: 'Type' as const, token: tokenInfo(ret), setOf: $.OPTION(() => tokenInfo($.CONSUME(SetOf))), type: $.SUBRULE($.columnTypeRule)})}, + {ALT: () => removeUndefined({kind: 'Type' as const, token: tokenInfo(ret), setOf: $.OPTION(() => ({token: tokenInfo($.CONSUME(SetOf))})), type: $.SUBRULE($.columnTypeRule)})}, {ALT: () => { const token = tokenInfo2(ret, $.CONSUME(Table)) $.CONSUME(ParenLeft) @@ -656,12 +656,12 @@ class PostgresParser extends EmbeddedActionsParser { this.createIndexStatementRule = $.RULE<(create: IToken) => CreateIndexStatementAst>('createIndexStatementRule', (create: IToken) => { // https://www.postgresql.org/docs/current/sql-createindex.html - const unique = $.OPTION(() => tokenInfo($.CONSUME(Unique))) + const unique = $.OPTION(() => ({token: tokenInfo($.CONSUME(Unique))})) const token = tokenInfo2(create, $.CONSUME(Index)) - const concurrently = $.OPTION2(() => tokenInfo($.CONSUME(Concurrently))) + const concurrently = $.OPTION2(() => ({token: tokenInfo($.CONSUME(Concurrently))})) const name = $.OPTION3(() => ({ifNotExists: $.SUBRULE(ifNotExistsRule), index: $.SUBRULE($.identifierRule)})) $.CONSUME(On) - const only = $.OPTION4(() => tokenInfo($.CONSUME(Only))) + const only = $.OPTION4(() => ({token: tokenInfo($.CONSUME(Only))})) const object = $.SUBRULE($.objectNameRule) const using = $.OPTION5(() => ({token: tokenInfo($.CONSUME(Using)), method: $.SUBRULE2($.identifierRule)})) $.CONSUME(ParenLeft) @@ -702,7 +702,7 @@ class PostgresParser extends EmbeddedActionsParser { const query = $.SUBRULE(selectStatementInnerRule) const withData = $.OPTION3(() => { const with_ = $.CONSUME(With) - const no = $.OPTION4(() => tokenInfo($.CONSUME(No))) + const no = $.OPTION4(() => ({token: tokenInfo($.CONSUME(No))})) const data = $.CONSUME(Data) return {token: tokenInfo2(with_, data), no} }) @@ -809,7 +809,7 @@ class PostgresParser extends EmbeddedActionsParser { return removeEmpty({kind: 'CreateTable' as const, meta: tokenInfo2(create, end), token, mode, ifNotExists, ...object, columns: columns.filter(isNotUndefined), constraints: constraints.filter(isNotUndefined)}) }) - this.createTriggerStatementRule = $.RULE<(create: IToken, replace?: TokenInfo) => CreateTriggerStatementAst>('createTriggerStatementRule', (create: IToken, replace?: TokenInfo) => { + this.createTriggerStatementRule = $.RULE<(create: IToken, replace?: { token: TokenInfo }) => CreateTriggerStatementAst>('createTriggerStatementRule', (create: IToken, replace?: { token: TokenInfo }) => { // https://www.postgresql.org/docs/current/sql-createtrigger.html const constraint = $.OPTION(() => ({token: tokenInfo($.CONSUME(Constraint))})) const token = tokenInfo2(create, $.CONSUME(Trigger)) @@ -910,10 +910,10 @@ class PostgresParser extends EmbeddedActionsParser { return removeEmpty({kind: 'CreateType' as const, meta: tokenInfo2(create, end), token, ...object, ...content}) }) - this.createViewStatementRule = $.RULE<(create: IToken, replace?: TokenInfo) => CreateViewStatementAst>('createViewStatementRule', (create: IToken, replace?: TokenInfo) => { + this.createViewStatementRule = $.RULE<(create: IToken, replace?: { token: TokenInfo }) => CreateViewStatementAst>('createViewStatementRule', (create: IToken, replace?: { token: TokenInfo }) => { // https://www.postgresql.org/docs/current/sql-createview.html - const temporary = $.OPTION2(() => $.OR([{ALT: () => tokenInfo($.CONSUME(Temp))}, {ALT: () => tokenInfo($.CONSUME(Temporary))}])) - const recursive = $.OPTION3(() => tokenInfo($.CONSUME(Recursive))) + const temporary = $.OPTION2(() => ({token: $.OR([{ALT: () => tokenInfo($.CONSUME(Temp))}, {ALT: () => tokenInfo($.CONSUME(Temporary))}])})) + const recursive = $.OPTION3(() => ({token: tokenInfo($.CONSUME(Recursive))})) const token = tokenInfo2(create, $.CONSUME(View)) const object = $.SUBRULE($.objectNameRule) const columns: IdentifierAst[] = [] @@ -931,11 +931,11 @@ class PostgresParser extends EmbeddedActionsParser { this.deleteStatementRule = $.RULE<() => DeleteStatementAst>('deleteStatementRule', () => { const start = $.CONSUME(Delete) const token = tokenInfo2(start, $.CONSUME(From)) - const only = $.OPTION(() => tokenInfo($.CONSUME(Only))) + const only = $.OPTION(() => ({token: tokenInfo($.CONSUME(Only))})) const object = $.SUBRULE($.objectNameRule) - const descendants = $.OPTION2(() => tokenInfo($.CONSUME(Asterisk))) + const descendants = $.OPTION2(() => ({token: tokenInfo($.CONSUME(Asterisk))})) const alias = $.OPTION3(() => $.SUBRULE($.aliasRule)) - const using = $.OPTION4(() => ({token: tokenInfo($.CONSUME(Using)), ...$.SUBRULE(fromItemRule)})) + const using = $.OPTION4(() => ({token: tokenInfo($.CONSUME(Using)), ...$.SUBRULE(fromClauseItemRule)})) const where = $.OPTION5(() => $.SUBRULE($.whereClauseRule)) const returning = $.OPTION6(() => $.SUBRULE(returningClauseRule)) const end = $.CONSUME(Semicolon) @@ -952,7 +952,7 @@ class PostgresParser extends EmbeddedActionsParser { {ALT: () => ({token: tokenInfo2(start, $.CONSUME(Type)), object: 'Type' as const})}, // https://www.postgresql.org/docs/current/sql-droptype.html {ALT: () => ({token: tokenInfo2(start, $.CONSUME(View)), object: 'View' as const})}, // https://www.postgresql.org/docs/current/sql-dropview.html ]) - const concurrently = $.OPTION(() => tokenInfo($.CONSUME(Concurrently))) + const concurrently = $.OPTION(() => ({token: tokenInfo($.CONSUME(Concurrently))})) const ifExists = $.SUBRULE(ifExistsRule) const objects: ObjectNameAst[] = [] $.AT_LEAST_ONE_SEP({SEP: Comma, DEF: () => objects.push($.SUBRULE($.objectNameRule))}) @@ -1031,7 +1031,7 @@ class PostgresParser extends EmbeddedActionsParser { const end = $.CONSUME(Semicolon) return removeUndefined({kind: 'Select' as const, meta: mergePositions([select?.token, tokenInfo(end)]), ...select}) }) - const selectStatementInnerRule = $.RULE<() => SelectStatementInnerAst>('selectStatementInnerRule', (): SelectStatementInnerAst => { + const selectStatementInnerRule = $.RULE<() => SelectInnerAst>('selectStatementInnerRule', (): SelectInnerAst => { // https://www.postgresql.org/docs/current/sql-select.html return $.OR([ {ALT: () => { @@ -1049,7 +1049,7 @@ class PostgresParser extends EmbeddedActionsParser { }}, ]) }) - const selectStatementMainRule = $.RULE<() => SelectStatementMainAst>('selectStatementMainRule', (): SelectStatementInnerAst => { + const selectStatementMainRule = $.RULE<() => SelectMainAst>('selectStatementMainRule', (): SelectInnerAst => { const select = $.SUBRULE($.selectClauseRule) const from = $.OPTION(() => $.SUBRULE($.fromClauseRule)) const where = $.OPTION2(() => $.SUBRULE($.whereClauseRule)) @@ -1059,7 +1059,7 @@ class PostgresParser extends EmbeddedActionsParser { $.MANY(() => window.push($.SUBRULE(windowClauseRule))) return removeEmpty({...select, from, where, groupBy, having, window}) }) - const selectStatementResultRule = $.RULE<() => SelectStatementResultAst>('selectStatementResultRule', () => { + const selectStatementResultRule = $.RULE<() => SelectResultAst>('selectStatementResultRule', () => { const union = $.OPTION(() => $.SUBRULE3(unionClauseRule)) const orderBy = $.OPTION2(() => $.SUBRULE(orderByClauseRule)) const limit = $.OPTION3(() => $.SUBRULE(limitClauseRule)) @@ -1107,9 +1107,9 @@ class PostgresParser extends EmbeddedActionsParser { this.updateStatementRule = $.RULE<() => UpdateStatementAst>('updateStatementRule', () => { // https://www.postgresql.org/docs/current/sql-update.html const start = $.CONSUME(Update) - const only = $.OPTION(() => tokenInfo($.CONSUME(Only))) + const only = $.OPTION(() => ({token: tokenInfo($.CONSUME(Only))})) const object = $.SUBRULE($.objectNameRule) - const descendants = $.OPTION2(() => tokenInfo($.CONSUME(Asterisk))) + const descendants = $.OPTION2(() => ({token: tokenInfo($.CONSUME(Asterisk))})) const alias = $.OPTION3(() => $.SUBRULE($.aliasRule)) $.CONSUME(Set) const columns = $.SUBRULE(updateColumnsRule) @@ -1118,8 +1118,8 @@ class PostgresParser extends EmbeddedActionsParser { const end = $.CONSUME(Semicolon) return removeUndefined({kind: 'Update' as const, meta: tokenInfo2(start, end), token: tokenInfo(start), only, schema: object.schema, table: object.name, descendants, alias, columns, where, returning}) }) - const updateColumnsRule = $.RULE<() => UpdateColumnAst[]>('updateColumnsRule', () => { - const columns: UpdateColumnAst[] = [] + const updateColumnsRule = $.RULE<() => ColumnUpdateAst[]>('updateColumnsRule', () => { + const columns: ColumnUpdateAst[] = [] $.AT_LEAST_ONE_SEP({SEP: Comma, DEF: () => { const column = $.SUBRULE($.identifierRule) $.CONSUME(Equal) @@ -1175,30 +1175,30 @@ class PostgresParser extends EmbeddedActionsParser { this.fromClauseRule = $.RULE<() => FromClauseAst>('fromClauseRule', () => { const token = tokenInfo($.CONSUME(From)) - const item = $.SUBRULE(fromItemRule) - const joins: FromJoinAst[] = [] - $.MANY({DEF: () => joins.push($.SUBRULE(fromJoinRule))}) + const item = $.SUBRULE(fromClauseItemRule) + const joins: FromClauseJoinAst[] = [] + $.MANY({DEF: () => joins.push($.SUBRULE(fromClauseJoinRule))}) return removeEmpty({token, ...item, joins}) }) - const fromItemRule = $.RULE<() => FromItemAst>('fromItemRule', () => { + const fromClauseItemRule = $.RULE<() => FromClauseItemAst>('fromClauseItemRule', () => { const item = $.OR([ - {ALT: () => $.SUBRULE(fromTableRule)}, - {ALT: () => $.SUBRULE(fromQueryRule)}, + {ALT: () => $.SUBRULE(fromClauseTableRule)}, + {ALT: () => $.SUBRULE(fromClauseQueryRule)}, ]) const alias = $.OPTION(() => $.SUBRULE($.aliasRule)) return removeUndefined({...item, alias}) }) - const fromTableRule = $.RULE<() => FromTableAst>('fromTableRule', () => { + const fromClauseTableRule = $.RULE<() => FromClauseTableAst>('fromClauseTableRule', () => { const object = $.SUBRULE($.objectNameRule) return removeUndefined({kind: 'Table' as const, schema: object.schema, table: object.name}) }) - const fromQueryRule = $.RULE<() => FromQueryAst>('fromQueryRule', () => { + const fromClauseQueryRule = $.RULE<() => FromClauseQueryAst>('fromClauseQueryRule', () => { $.CONSUME(ParenLeft) const select = $.SUBRULE(selectStatementInnerRule) $.CONSUME(ParenRight) return {kind: 'Select', select} }) - const fromJoinRule = $.RULE<() => FromJoinAst>('fromJoinRule', () => { + const fromClauseJoinRule = $.RULE<() => FromClauseJoinAst>('fromClauseJoinRule', () => { const natural = $.OPTION(() => ({kind: 'Natural', token: tokenInfo($.CONSUME(Natural))})) const {kind, token} = $.OR([ {ALT: () => ({kind: 'Inner' as const, token: tokenInfo2($.OPTION2(() => $.CONSUME(Inner)), $.CONSUME(Join))})}, @@ -1207,7 +1207,7 @@ class PostgresParser extends EmbeddedActionsParser { {ALT: () => ({kind: 'Full' as const, token: tokenInfo3($.CONSUME(Full), $.OPTION5(() => $.CONSUME3(Outer)), $.CONSUME4(Join))})}, {ALT: () => ({kind: 'Cross' as const, token: tokenInfo2($.CONSUME(Cross), $.CONSUME5(Join))})}, ]) - const from = $.SUBRULE(fromItemRule) + const from = $.SUBRULE(fromClauseItemRule) const on = $.OPTION6(() => $.OR2([ {ALT: () => ({kind: 'On', token: tokenInfo($.CONSUME(On)), predicate: $.SUBRULE($.expressionRule)})}, {ALT: () => { @@ -1470,7 +1470,7 @@ class PostgresParser extends EmbeddedActionsParser { return params }) - const constraintNameRule = $.RULE<() => ConstraintNameAst>('constraintNameRule', () => ({token: tokenInfo($.CONSUME(Constraint)), name: $.SUBRULE($.identifierRule)})) + const constraintNameRule = $.RULE<() => NameAst>('constraintNameRule', () => ({token: tokenInfo($.CONSUME(Constraint)), name: $.SUBRULE($.identifierRule)})) const foreignKeyActionsRule = $.RULE<() => ForeignKeyActionAst>('foreignKeyActionsRule', () => { const action = $.OR([ {ALT: () => ({kind: 'NoAction' as const, token: tokenInfo($.CONSUME(NoAction))})}, @@ -1490,8 +1490,8 @@ class PostgresParser extends EmbeddedActionsParser { return columns.filter(isNotUndefined) }) - const ifExistsRule = $.RULE<() => TokenInfo | undefined>('ifExistsRule', () => $.OPTION(() => tokenInfo2($.CONSUME(If), $.CONSUME(Exists)))) - const ifNotExistsRule = $.RULE<() => TokenInfo | undefined>('ifNotExistsRule', () => $.OPTION(() => tokenInfo3($.CONSUME(If), $.CONSUME(Not), $.CONSUME(Exists)))) + const ifExistsRule = $.RULE<() => {token: TokenInfo} | undefined>('ifExistsRule', () => $.OPTION(() => ({token: tokenInfo2($.CONSUME(If), $.CONSUME(Exists))}))) + const ifNotExistsRule = $.RULE<() => {token: TokenInfo} | undefined>('ifNotExistsRule', () => $.OPTION(() => ({token: tokenInfo3($.CONSUME(If), $.CONSUME(Not), $.CONSUME(Exists))}))) // basic parts @@ -1736,10 +1736,10 @@ class PostgresParser extends EmbeddedActionsParser { }}, {ALT: () => ({name: toIdentifier($.CONSUME(With))})}, // needed for `with time zone` ]))}) - const array = $.OPTION3(() => tokenInfo2($.CONSUME(BracketLeft), $.CONSUME(BracketRight))) + const array = $.OPTION3(() => ({token: tokenInfo2($.CONSUME(BracketLeft), $.CONSUME(BracketRight))})) const name = { kind: 'Identifier' as const, - token: mergePositions(parts.flatMap(p => [p.name?.token, p.last]).concat([array])), + token: mergePositions(parts.flatMap(p => [p.name?.token, p.last]).concat([array?.token])), value: parts.filter(isNotUndefined).map(p => p.name?.value + (p.args ? `(${p.args.map(v => v.value).join(', ')})` : '')).join(' ') + (array ? '[]' : '') } return removeEmpty({schema, name, args: parts.flatMap(p => p.args || []), array, token: mergePositions([schema?.token, name.token])})