diff --git a/ast/alter_database_set_statement.go b/ast/alter_database_set_statement.go index 7581ff21..b2c3bfec 100644 --- a/ast/alter_database_set_statement.go +++ b/ast/alter_database_set_statement.go @@ -35,6 +35,35 @@ type OnOffDatabaseOption struct { func (o *OnOffDatabaseOption) node() {} func (o *OnOffDatabaseOption) databaseOption() {} +// DelayedDurabilityDatabaseOption represents DELAYED_DURABILITY option +type DelayedDurabilityDatabaseOption struct { + OptionKind string // "DelayedDurability" + Value string // "Disabled", "Allowed", "Forced" +} + +func (d *DelayedDurabilityDatabaseOption) node() {} +func (d *DelayedDurabilityDatabaseOption) databaseOption() {} + +// IdentifierDatabaseOption represents a database option with an identifier value +type IdentifierDatabaseOption struct { + OptionKind string `json:"OptionKind,omitempty"` // "CatalogCollation" + Value *Identifier `json:"Value,omitempty"` +} + +func (i *IdentifierDatabaseOption) node() {} +func (i *IdentifierDatabaseOption) databaseOption() {} + +// CreateDatabaseOption is an interface for CREATE DATABASE options (can be DatabaseOption) +type CreateDatabaseOption interface { + node() + createDatabaseOption() +} + +// Make existing database options implement CreateDatabaseOption +func (o *OnOffDatabaseOption) createDatabaseOption() {} +func (i *IdentifierDatabaseOption) createDatabaseOption() {} +func (d *DelayedDurabilityDatabaseOption) createDatabaseOption() {} + // AlterDatabaseAddFileStatement represents ALTER DATABASE ... ADD FILE statement type AlterDatabaseAddFileStatement struct { DatabaseName *Identifier @@ -97,6 +126,7 @@ func (a *AlterDatabaseRemoveFileStatement) statement() {} type AlterDatabaseRemoveFileGroupStatement struct { DatabaseName *Identifier FileGroupName *Identifier + UseCurrent bool } func (a *AlterDatabaseRemoveFileGroupStatement) node() {} diff --git a/ast/alter_simple_statements.go b/ast/alter_simple_statements.go index 2f338350..1c76508c 100644 --- a/ast/alter_simple_statements.go +++ b/ast/alter_simple_statements.go @@ -48,7 +48,8 @@ func (s *AlterCertificateStatement) statement() {} // AlterApplicationRoleStatement represents an ALTER APPLICATION ROLE statement. type AlterApplicationRoleStatement struct { - Name *Identifier `json:"Name,omitempty"` + Name *Identifier `json:"Name,omitempty"` + ApplicationRoleOptions []*ApplicationRoleOption `json:"ApplicationRoleOptions,omitempty"` } func (s *AlterApplicationRoleStatement) node() {} @@ -64,7 +65,8 @@ func (s *AlterAsymmetricKeyStatement) statement() {} // AlterQueueStatement represents an ALTER QUEUE statement. type AlterQueueStatement struct { - Name *SchemaObjectName `json:"Name,omitempty"` + Name *SchemaObjectName `json:"Name,omitempty"` + QueueOptions []QueueOption `json:"QueueOptions,omitempty"` } func (s *AlterQueueStatement) node() {} diff --git a/ast/create_simple_statements.go b/ast/create_simple_statements.go index 077af431..377722da 100644 --- a/ast/create_simple_statements.go +++ b/ast/create_simple_statements.go @@ -2,7 +2,9 @@ package ast // CreateDatabaseStatement represents a CREATE DATABASE statement. type CreateDatabaseStatement struct { - DatabaseName *Identifier `json:"DatabaseName,omitempty"` + DatabaseName *Identifier `json:"DatabaseName,omitempty"` + Options []CreateDatabaseOption `json:"Options,omitempty"` + AttachMode string `json:"AttachMode,omitempty"` // "None", "Attach", "AttachRebuildLog" } func (s *CreateDatabaseStatement) node() {} @@ -24,9 +26,33 @@ type CreateServiceStatement struct { func (s *CreateServiceStatement) node() {} func (s *CreateServiceStatement) statement() {} +// QueueOption is an interface for queue options. +type QueueOption interface { + node() + queueOption() +} + +// QueueStateOption represents a queue state option (STATUS, RETENTION, POISON_MESSAGE_HANDLING). +type QueueStateOption struct { + OptionState string `json:"OptionState,omitempty"` // "On" or "Off" + OptionKind string `json:"OptionKind,omitempty"` // "Status", "Retention", "PoisonMessageHandlingStatus" +} + +func (o *QueueStateOption) node() {} +func (o *QueueStateOption) queueOption() {} + +// QueueOptionSimple represents a simple queue option like ActivationDrop. +type QueueOptionSimple struct { + OptionKind string `json:"OptionKind,omitempty"` // e.g. "ActivationDrop" +} + +func (o *QueueOptionSimple) node() {} +func (o *QueueOptionSimple) queueOption() {} + // CreateQueueStatement represents a CREATE QUEUE statement. type CreateQueueStatement struct { - Name *SchemaObjectName `json:"Name,omitempty"` + Name *SchemaObjectName `json:"Name,omitempty"` + QueueOptions []QueueOption `json:"QueueOptions,omitempty"` } func (s *CreateQueueStatement) node() {} @@ -82,7 +108,10 @@ func (s *CreateSymmetricKeyStatement) statement() {} // CreateMessageTypeStatement represents a CREATE MESSAGE TYPE statement. type CreateMessageTypeStatement struct { - Name *Identifier `json:"Name,omitempty"` + Name *Identifier `json:"Name,omitempty"` + Owner *Identifier `json:"Owner,omitempty"` + ValidationMethod string `json:"ValidationMethod,omitempty"` + XmlSchemaCollectionName *SchemaObjectName `json:"XmlSchemaCollectionName,omitempty"` } func (s *CreateMessageTypeStatement) node() {} @@ -98,12 +127,21 @@ func (s *CreateRemoteServiceBindingStatement) statement() {} // CreateApplicationRoleStatement represents a CREATE APPLICATION ROLE statement. type CreateApplicationRoleStatement struct { - Name *Identifier `json:"Name,omitempty"` + Name *Identifier `json:"Name,omitempty"` + ApplicationRoleOptions []*ApplicationRoleOption `json:"ApplicationRoleOptions,omitempty"` } func (s *CreateApplicationRoleStatement) node() {} func (s *CreateApplicationRoleStatement) statement() {} +// ApplicationRoleOption represents an option in CREATE/ALTER APPLICATION ROLE +type ApplicationRoleOption struct { + OptionKind string `json:"OptionKind,omitempty"` + Value *IdentifierOrValueExpression `json:"Value,omitempty"` +} + +func (o *ApplicationRoleOption) node() {} + // CreateFulltextCatalogStatement represents a CREATE FULLTEXT CATALOG statement. type CreateFulltextCatalogStatement struct { Name *Identifier `json:"Name,omitempty"` @@ -150,8 +188,11 @@ func (s *CreateIndexStatement) statement() {} // CreateStatisticsStatement represents a CREATE STATISTICS statement. type CreateStatisticsStatement struct { - Name *Identifier `json:"Name,omitempty"` - OnName *SchemaObjectName `json:"OnName,omitempty"` + Name *Identifier `json:"Name,omitempty"` + OnName *SchemaObjectName `json:"OnName,omitempty"` + Columns []*ColumnReferenceExpression `json:"Columns,omitempty"` + StatisticsOptions []StatisticsOption `json:"StatisticsOptions,omitempty"` + FilterPredicate BooleanExpression `json:"FilterPredicate,omitempty"` } func (s *CreateStatisticsStatement) node() {} diff --git a/ast/cursor_statements.go b/ast/cursor_statements.go new file mode 100644 index 00000000..85fa1b5c --- /dev/null +++ b/ast/cursor_statements.go @@ -0,0 +1,50 @@ +package ast + +// FetchType represents the orientation for a FETCH statement. +type FetchType struct { + Orientation string `json:"Orientation,omitempty"` + RowOffset ScalarExpression `json:"RowOffset,omitempty"` +} + +// DeclareCursorStatement represents DECLARE cursor_name CURSOR FOR SELECT. +type DeclareCursorStatement struct { + Name *Identifier `json:"Name,omitempty"` + CursorDefinition *CursorDefinition `json:"CursorDefinition,omitempty"` +} + +func (s *DeclareCursorStatement) node() {} +func (s *DeclareCursorStatement) statement() {} + +// OpenCursorStatement represents OPEN cursor_name. +type OpenCursorStatement struct { + Cursor *CursorId `json:"Cursor,omitempty"` +} + +func (s *OpenCursorStatement) node() {} +func (s *OpenCursorStatement) statement() {} + +// CloseCursorStatement represents CLOSE cursor_name. +type CloseCursorStatement struct { + Cursor *CursorId `json:"Cursor,omitempty"` +} + +func (s *CloseCursorStatement) node() {} +func (s *CloseCursorStatement) statement() {} + +// DeallocateCursorStatement represents DEALLOCATE cursor_name. +type DeallocateCursorStatement struct { + Cursor *CursorId `json:"Cursor,omitempty"` +} + +func (s *DeallocateCursorStatement) node() {} +func (s *DeallocateCursorStatement) statement() {} + +// FetchCursorStatement represents FETCH cursor_name. +type FetchCursorStatement struct { + FetchType *FetchType `json:"FetchType,omitempty"` + Cursor *CursorId `json:"Cursor,omitempty"` + IntoVariables []ScalarExpression `json:"IntoVariables,omitempty"` +} + +func (s *FetchCursorStatement) node() {} +func (s *FetchCursorStatement) statement() {} diff --git a/ast/enable_disable_trigger_statement.go b/ast/enable_disable_trigger_statement.go new file mode 100644 index 00000000..1cc75e42 --- /dev/null +++ b/ast/enable_disable_trigger_statement.go @@ -0,0 +1,12 @@ +package ast + +// EnableDisableTriggerStatement represents ENABLE/DISABLE TRIGGER statements +type EnableDisableTriggerStatement struct { + TriggerEnforcement string // "Enable" or "Disable" + All bool // true if ENABLE/DISABLE TRIGGER ALL + TriggerNames []*SchemaObjectName + TriggerObject *TriggerObject +} + +func (s *EnableDisableTriggerStatement) statement() {} +func (s *EnableDisableTriggerStatement) node() {} diff --git a/ast/update_statistics_statement.go b/ast/update_statistics_statement.go new file mode 100644 index 00000000..8e24c3a4 --- /dev/null +++ b/ast/update_statistics_statement.go @@ -0,0 +1,47 @@ +package ast + +// UpdateStatisticsStatement represents UPDATE STATISTICS. +type UpdateStatisticsStatement struct { + SchemaObjectName *SchemaObjectName `json:"SchemaObjectName,omitempty"` + SubElements []*Identifier `json:"SubElements,omitempty"` + StatisticsOptions []StatisticsOption `json:"StatisticsOptions,omitempty"` +} + +func (u *UpdateStatisticsStatement) node() {} +func (u *UpdateStatisticsStatement) statement() {} + +// StatisticsOption is an interface for statistics options. +type StatisticsOption interface { + statisticsOption() +} + +// SimpleStatisticsOption represents a simple statistics option like ALL, FULLSCAN, etc. +type SimpleStatisticsOption struct { + OptionKind string `json:"OptionKind,omitempty"` +} + +func (s *SimpleStatisticsOption) statisticsOption() {} + +// LiteralStatisticsOption represents a statistics option with a literal value. +type LiteralStatisticsOption struct { + OptionKind string `json:"OptionKind,omitempty"` + Literal ScalarExpression `json:"Literal,omitempty"` +} + +func (l *LiteralStatisticsOption) statisticsOption() {} + +// OnOffStatisticsOption represents a statistics option with ON/OFF value. +type OnOffStatisticsOption struct { + OptionKind string `json:"OptionKind,omitempty"` + OptionState string `json:"OptionState,omitempty"` +} + +func (o *OnOffStatisticsOption) statisticsOption() {} + +// ResampleStatisticsOption represents RESAMPLE statistics option. +type ResampleStatisticsOption struct { + OptionKind string `json:"OptionKind,omitempty"` + Partitions []ScalarExpression `json:"Partitions,omitempty"` +} + +func (r *ResampleStatisticsOption) statisticsOption() {} diff --git a/parser/marshal.go b/parser/marshal.go index 3e610c22..af408703 100644 --- a/parser/marshal.go +++ b/parser/marshal.go @@ -54,6 +54,8 @@ func statementToJSON(stmt ast.Statement) jsonNode { return insertStatementToJSON(s) case *ast.UpdateStatement: return updateStatementToJSON(s) + case *ast.UpdateStatisticsStatement: + return updateStatisticsStatementToJSON(s) case *ast.DeleteStatement: return deleteStatementToJSON(s) case *ast.DeclareVariableStatement: @@ -322,6 +324,8 @@ func statementToJSON(stmt ast.Statement) jsonNode { return alterTriggerStatementToJSON(s) case *ast.CreateTriggerStatement: return createTriggerStatementToJSON(s) + case *ast.EnableDisableTriggerStatement: + return enableDisableTriggerStatementToJSON(s) case *ast.CreateDatabaseStatement: return createDatabaseStatementToJSON(s) case *ast.CreateLoginStatement: @@ -430,6 +434,16 @@ func statementToJSON(stmt ast.Statement) jsonNode { return alterServiceMasterKeyStatementToJSON(s) case *ast.RenameEntityStatement: return renameEntityStatementToJSON(s) + case *ast.OpenCursorStatement: + return openCursorStatementToJSON(s) + case *ast.CloseCursorStatement: + return closeCursorStatementToJSON(s) + case *ast.DeallocateCursorStatement: + return deallocateCursorStatementToJSON(s) + case *ast.FetchCursorStatement: + return fetchCursorStatementToJSON(s) + case *ast.DeclareCursorStatement: + return declareCursorStatementToJSON(s) default: return jsonNode{"$type": "UnknownStatement"} } @@ -777,6 +791,12 @@ func databaseOptionToJSON(opt ast.DatabaseOption) jsonNode { "OptionKind": o.OptionKind, "OptionState": o.OptionState, } + case *ast.DelayedDurabilityDatabaseOption: + return jsonNode{ + "$type": "DelayedDurabilityDatabaseOption", + "Value": o.Value, + "OptionKind": o.OptionKind, + } default: return jsonNode{"$type": "UnknownDatabaseOption"} } @@ -5101,6 +5121,25 @@ func triggerActionToJSON(a *ast.TriggerAction) jsonNode { return node } +func enableDisableTriggerStatementToJSON(s *ast.EnableDisableTriggerStatement) jsonNode { + node := jsonNode{ + "$type": "EnableDisableTriggerStatement", + "TriggerEnforcement": s.TriggerEnforcement, + "All": s.All, + } + if len(s.TriggerNames) > 0 { + names := make([]jsonNode, len(s.TriggerNames)) + for i, n := range s.TriggerNames { + names[i] = schemaObjectNameToJSON(n) + } + node["TriggerNames"] = names + } + if s.TriggerObject != nil { + node["TriggerObject"] = triggerObjectToJSON(s.TriggerObject) + } + return node +} + func alterIndexStatementToJSON(s *ast.AlterIndexStatement) jsonNode { node := jsonNode{ "$type": "AlterIndexStatement", @@ -5873,6 +5912,13 @@ func alterApplicationRoleStatementToJSON(s *ast.AlterApplicationRoleStatement) j if s.Name != nil { node["Name"] = identifierToJSON(s.Name) } + if len(s.ApplicationRoleOptions) > 0 { + opts := make([]jsonNode, len(s.ApplicationRoleOptions)) + for i, opt := range s.ApplicationRoleOptions { + opts[i] = applicationRoleOptionToJSON(opt) + } + node["ApplicationRoleOptions"] = opts + } return node } @@ -5893,6 +5939,13 @@ func alterQueueStatementToJSON(s *ast.AlterQueueStatement) jsonNode { if s.Name != nil { node["Name"] = schemaObjectNameToJSON(s.Name) } + if len(s.QueueOptions) > 0 { + opts := make([]jsonNode, len(s.QueueOptions)) + for i, opt := range s.QueueOptions { + opts[i] = queueOptionToJSON(opt) + } + node["QueueOptions"] = opts + } return node } @@ -6017,9 +6070,42 @@ func createDatabaseStatementToJSON(s *ast.CreateDatabaseStatement) jsonNode { if s.DatabaseName != nil { node["DatabaseName"] = identifierToJSON(s.DatabaseName) } + if len(s.Options) > 0 { + opts := make([]jsonNode, len(s.Options)) + for i, opt := range s.Options { + opts[i] = createDatabaseOptionToJSON(opt) + } + node["Options"] = opts + // Only output AttachMode when there are options + if s.AttachMode != "" { + node["AttachMode"] = s.AttachMode + } + } return node } +func createDatabaseOptionToJSON(opt ast.CreateDatabaseOption) jsonNode { + switch o := opt.(type) { + case *ast.OnOffDatabaseOption: + return jsonNode{ + "$type": "OnOffDatabaseOption", + "OptionState": o.OptionState, + "OptionKind": o.OptionKind, + } + case *ast.IdentifierDatabaseOption: + node := jsonNode{ + "$type": "IdentifierDatabaseOption", + "OptionKind": o.OptionKind, + } + if o.Value != nil { + node["Value"] = identifierToJSON(o.Value) + } + return node + default: + return jsonNode{"$type": "CreateDatabaseOption"} + } +} + func createLoginStatementToJSON(s *ast.CreateLoginStatement) jsonNode { node := jsonNode{ "$type": "CreateLoginStatement", @@ -6080,6 +6166,15 @@ func createMessageTypeStatementToJSON(s *ast.CreateMessageTypeStatement) jsonNod if s.Name != nil { node["Name"] = identifierToJSON(s.Name) } + if s.Owner != nil { + node["Owner"] = identifierToJSON(s.Owner) + } + if s.ValidationMethod != "" { + node["ValidationMethod"] = s.ValidationMethod + } + if s.XmlSchemaCollectionName != nil { + node["XmlSchemaCollectionName"] = schemaObjectNameToJSON(s.XmlSchemaCollectionName) + } return node } @@ -6100,9 +6195,36 @@ func createQueueStatementToJSON(s *ast.CreateQueueStatement) jsonNode { if s.Name != nil { node["Name"] = schemaObjectNameToJSON(s.Name) } + if len(s.QueueOptions) > 0 { + opts := make([]jsonNode, len(s.QueueOptions)) + for i, opt := range s.QueueOptions { + opts[i] = queueOptionToJSON(opt) + } + node["QueueOptions"] = opts + } return node } +func queueOptionToJSON(opt ast.QueueOption) jsonNode { + switch o := opt.(type) { + case *ast.QueueStateOption: + node := jsonNode{ + "$type": "QueueStateOption", + "OptionState": o.OptionState, + "OptionKind": o.OptionKind, + } + return node + case *ast.QueueOptionSimple: + node := jsonNode{ + "$type": "QueueOption", + "OptionKind": o.OptionKind, + } + return node + default: + return jsonNode{"$type": "QueueOption"} + } +} + func createRouteStatementToJSON(s *ast.CreateRouteStatement) jsonNode { node := jsonNode{ "$type": "CreateRouteStatement", @@ -6140,6 +6262,24 @@ func createApplicationRoleStatementToJSON(s *ast.CreateApplicationRoleStatement) if s.Name != nil { node["Name"] = identifierToJSON(s.Name) } + if len(s.ApplicationRoleOptions) > 0 { + opts := make([]jsonNode, len(s.ApplicationRoleOptions)) + for i, opt := range s.ApplicationRoleOptions { + opts[i] = applicationRoleOptionToJSON(opt) + } + node["ApplicationRoleOptions"] = opts + } + return node +} + +func applicationRoleOptionToJSON(opt *ast.ApplicationRoleOption) jsonNode { + node := jsonNode{ + "$type": "ApplicationRoleOption", + "OptionKind": opt.OptionKind, + } + if opt.Value != nil { + node["Value"] = identifierOrValueExpressionToJSON(opt.Value) + } return node } @@ -6183,6 +6323,23 @@ func createStatisticsStatementToJSON(s *ast.CreateStatisticsStatement) jsonNode if s.OnName != nil { node["OnName"] = schemaObjectNameToJSON(s.OnName) } + if len(s.Columns) > 0 { + cols := make([]jsonNode, len(s.Columns)) + for i, c := range s.Columns { + cols[i] = columnReferenceExpressionToJSON(c) + } + node["Columns"] = cols + } + if len(s.StatisticsOptions) > 0 { + opts := make([]jsonNode, len(s.StatisticsOptions)) + for i, o := range s.StatisticsOptions { + opts[i] = statisticsOptionToJSON(o) + } + node["StatisticsOptions"] = opts + } + if s.FilterPredicate != nil { + node["FilterPredicate"] = booleanExpressionToJSON(s.FilterPredicate) + } return node } @@ -6302,6 +6459,7 @@ func alterDatabaseModifyFileGroupStatementToJSON(s *ast.AlterDatabaseModifyFileG node["FileGroup"] = identifierToJSON(s.FileGroupName) } node["MakeDefault"] = s.MakeDefault + node["UseCurrent"] = false if s.NewFileGroupName != nil { node["NewFileGroupName"] = identifierToJSON(s.NewFileGroupName) } @@ -6347,6 +6505,7 @@ func alterDatabaseRemoveFileGroupStatementToJSON(s *ast.AlterDatabaseRemoveFileG if s.FileGroupName != nil { node["FileGroup"] = identifierToJSON(s.FileGroupName) } + node["UseCurrent"] = s.UseCurrent return node } @@ -6581,3 +6740,195 @@ func alterExternalLibraryStatementToJSON(s *ast.AlterExternalLibraryStatement) j return node } +func fetchTypeToJSON(f *ast.FetchType) jsonNode { + node := jsonNode{ + "$type": "FetchType", + } + if f.Orientation != "" { + node["Orientation"] = f.Orientation + } + if f.RowOffset != nil { + node["RowOffset"] = scalarExpressionToJSON(f.RowOffset) + } + return node +} + +func openCursorStatementToJSON(s *ast.OpenCursorStatement) jsonNode { + node := jsonNode{ + "$type": "OpenCursorStatement", + } + if s.Cursor != nil { + node["Cursor"] = cursorIdToJSON(s.Cursor) + } + return node +} + +func closeCursorStatementToJSON(s *ast.CloseCursorStatement) jsonNode { + node := jsonNode{ + "$type": "CloseCursorStatement", + } + if s.Cursor != nil { + node["Cursor"] = cursorIdToJSON(s.Cursor) + } + return node +} + +func deallocateCursorStatementToJSON(s *ast.DeallocateCursorStatement) jsonNode { + node := jsonNode{ + "$type": "DeallocateCursorStatement", + } + if s.Cursor != nil { + node["Cursor"] = cursorIdToJSON(s.Cursor) + } + return node +} + +func fetchCursorStatementToJSON(s *ast.FetchCursorStatement) jsonNode { + node := jsonNode{ + "$type": "FetchCursorStatement", + } + if s.FetchType != nil { + node["FetchType"] = fetchTypeToJSON(s.FetchType) + } + if s.Cursor != nil { + node["Cursor"] = cursorIdToJSON(s.Cursor) + } + if len(s.IntoVariables) > 0 { + vars := make([]jsonNode, len(s.IntoVariables)) + for i, v := range s.IntoVariables { + vars[i] = scalarExpressionToJSON(v) + } + node["IntoVariables"] = vars + } + return node +} + +func updateStatisticsStatementToJSON(s *ast.UpdateStatisticsStatement) jsonNode { + node := jsonNode{ + "$type": "UpdateStatisticsStatement", + } + if s.SchemaObjectName != nil { + node["SchemaObjectName"] = schemaObjectNameToJSON(s.SchemaObjectName) + } + if len(s.SubElements) > 0 { + elems := make([]jsonNode, len(s.SubElements)) + for i, e := range s.SubElements { + elems[i] = identifierToJSON(e) + } + node["SubElements"] = elems + } + if len(s.StatisticsOptions) > 0 { + opts := make([]jsonNode, len(s.StatisticsOptions)) + for i, o := range s.StatisticsOptions { + opts[i] = statisticsOptionToJSON(o) + } + node["StatisticsOptions"] = opts + } + return node +} + +func statisticsOptionToJSON(opt ast.StatisticsOption) jsonNode { + switch o := opt.(type) { + case *ast.SimpleStatisticsOption: + return simpleStatisticsOptionToJSON(o) + case *ast.LiteralStatisticsOption: + return literalStatisticsOptionToJSON(o) + case *ast.OnOffStatisticsOption: + return onOffStatisticsOptionToJSON(o) + case *ast.ResampleStatisticsOption: + return resampleStatisticsOptionToJSON(o) + default: + return jsonNode{"$type": "UnknownStatisticsOption"} + } +} + +func simpleStatisticsOptionToJSON(o *ast.SimpleStatisticsOption) jsonNode { + node := jsonNode{ + "$type": "StatisticsOption", + } + if o.OptionKind != "" { + node["OptionKind"] = o.OptionKind + } + return node +} + +func literalStatisticsOptionToJSON(o *ast.LiteralStatisticsOption) jsonNode { + node := jsonNode{ + "$type": "LiteralStatisticsOption", + } + if o.OptionKind != "" { + node["OptionKind"] = o.OptionKind + } + if o.Literal != nil { + node["Literal"] = scalarExpressionToJSON(o.Literal) + } + return node +} + +func onOffStatisticsOptionToJSON(o *ast.OnOffStatisticsOption) jsonNode { + node := jsonNode{ + "$type": "OnOffStatisticsOption", + } + if o.OptionKind != "" { + node["OptionKind"] = o.OptionKind + } + if o.OptionState != "" { + node["OptionState"] = o.OptionState + } + return node +} + +func resampleStatisticsOptionToJSON(o *ast.ResampleStatisticsOption) jsonNode { + node := jsonNode{ + "$type": "ResampleStatisticsOption", + } + if o.OptionKind != "" { + node["OptionKind"] = o.OptionKind + } + if len(o.Partitions) > 0 { + partitions := make([]jsonNode, len(o.Partitions)) + for i, p := range o.Partitions { + partitions[i] = scalarExpressionToJSON(p) + } + node["Partitions"] = partitions + } + return node +} + +func declareCursorStatementToJSON(s *ast.DeclareCursorStatement) jsonNode { + node := jsonNode{ + "$type": "DeclareCursorStatement", + } + if s.Name != nil { + node["Name"] = identifierToJSON(s.Name) + } + if s.CursorDefinition != nil { + node["CursorDefinition"] = declareCursorDefinitionToJSON(s.CursorDefinition) + } + return node +} + +func declareCursorDefinitionToJSON(d *ast.CursorDefinition) jsonNode { + node := jsonNode{ + "$type": "CursorDefinition", + } + if len(d.Options) > 0 { + opts := make([]jsonNode, len(d.Options)) + for i, o := range d.Options { + opts[i] = jsonNode{ + "$type": "CursorOption", + "OptionKind": o.OptionKind, + } + } + node["Options"] = opts + } + if d.Select != nil { + // For DeclareCursorStatement, we need to wrap the QueryExpression in a SelectStatement format + selectNode := jsonNode{ + "$type": "SelectStatement", + "QueryExpression": queryExpressionToJSON(d.Select), + } + node["Select"] = selectNode + } + return node +} diff --git a/parser/parse_ddl.go b/parser/parse_ddl.go index fe0a4a34..a6476d71 100644 --- a/parser/parse_ddl.go +++ b/parser/parse_ddl.go @@ -1460,6 +1460,19 @@ func (p *Parser) parseAlterDatabaseSetStatement(dbName *ast.Identifier) (*ast.Al OptionState: capitalizeFirst(optionValue), } stmt.Options = append(stmt.Options, opt) + case "DELAYED_DURABILITY": + // This option uses = with DISABLED/ALLOWED/FORCED values + if p.curTok.Type != TokenEquals { + return nil, fmt.Errorf("expected = after %s, got %s", optionName, p.curTok.Literal) + } + p.nextToken() + optionValue := strings.ToUpper(p.curTok.Literal) + p.nextToken() + opt := &ast.DelayedDurabilityDatabaseOption{ + OptionKind: "DelayedDurability", + Value: capitalizeFirst(optionValue), + } + stmt.Options = append(stmt.Options, opt) default: // Handle generic options with = syntax (e.g., OPTIMIZED_LOCKING = ON) if p.curTok.Type == TokenEquals { @@ -1580,12 +1593,24 @@ func (p *Parser) parseAlterDatabaseModifyStatement(dbName *ast.Identifier) (ast. case "DEFAULT": stmt.MakeDefault = true p.nextToken() - case "READONLY", "READ_ONLY": + case "READONLY": + stmt.UpdatabilityOption = "ReadOnlyOld" + p.nextToken() + case "READ_ONLY": stmt.UpdatabilityOption = "ReadOnly" p.nextToken() - case "READWRITE", "READ_WRITE": + case "READWRITE": + stmt.UpdatabilityOption = "ReadWriteOld" + p.nextToken() + case "READ_WRITE": stmt.UpdatabilityOption = "ReadWrite" p.nextToken() + case "AUTOGROW_ALL_FILES": + stmt.UpdatabilityOption = "AutogrowAllFiles" + p.nextToken() + case "AUTOGROW_SINGLE_FILE": + stmt.UpdatabilityOption = "AutogrowSingleFile" + p.nextToken() case "NAME": p.nextToken() // consume NAME if p.curTok.Type == TokenEquals { @@ -3309,8 +3334,20 @@ func (p *Parser) parseAlterApplicationRoleStatement() (*ast.AlterApplicationRole // Parse role name stmt.Name = p.parseIdentifier() - // Skip rest of statement - p.skipToEndOfStatement() + // Optional WITH clause + if p.curTok.Type == TokenWith { + p.nextToken() + opts, err := p.parseApplicationRoleOptions() + if err != nil { + return nil, err + } + stmt.ApplicationRoleOptions = opts + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } return stmt, nil } @@ -3347,6 +3384,16 @@ func (p *Parser) parseAlterQueueStatement() (*ast.AlterQueueStatement, error) { } stmt.Name = name + // Check for WITH clause + if strings.ToUpper(p.curTok.Literal) == "WITH" { + p.nextToken() // consume WITH + opts, err := p.parseQueueOptions() + if err != nil { + return nil, err + } + stmt.QueueOptions = opts + } + // Skip rest of statement p.skipToEndOfStatement() diff --git a/parser/parse_dml.go b/parser/parse_dml.go index 89fea5d9..b071f7dc 100644 --- a/parser/parse_dml.go +++ b/parser/parse_dml.go @@ -1045,3 +1045,223 @@ func (p *Parser) parseIdentifierOrValueExpression() (*ast.IdentifierOrValueExpre return result, nil } +// parseUpdateOrUpdateStatisticsStatement routes to UPDATE or UPDATE STATISTICS. +func (p *Parser) parseUpdateOrUpdateStatisticsStatement() (ast.Statement, error) { + // Consume UPDATE + p.nextToken() + + // Check for UPDATE STATISTICS + if p.curTok.Type == TokenStats || strings.ToUpper(p.curTok.Literal) == "STATISTICS" { + return p.parseUpdateStatisticsStatementContinued() + } + + // Otherwise, parse normal UPDATE statement + stmt := &ast.UpdateStatement{ + UpdateSpecification: &ast.UpdateSpecification{}, + } + + // Parse target + target, err := p.parseDMLTarget() + if err != nil { + return nil, err + } + stmt.UpdateSpecification.Target = target + + // Expect SET + if p.curTok.Type != TokenSet { + return nil, fmt.Errorf("expected SET, got %s", p.curTok.Literal) + } + p.nextToken() + + // Parse SET clauses + setClauses, err := p.parseSetClauses() + if err != nil { + return nil, err + } + stmt.UpdateSpecification.SetClauses = setClauses + + // Parse optional FROM clause + if p.curTok.Type == TokenFrom { + fromClause, err := p.parseFromClause() + if err != nil { + return nil, err + } + stmt.UpdateSpecification.FromClause = fromClause + } + + // Parse optional WHERE clause + if p.curTok.Type == TokenWhere { + whereClause, err := p.parseWhereClause() + if err != nil { + return nil, err + } + stmt.UpdateSpecification.WhereClause = whereClause + } + + // Parse optional OPTION clause + if p.curTok.Type == TokenOption { + hints, err := p.parseOptionClause() + if err != nil { + return nil, err + } + stmt.OptimizerHints = hints + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + + return stmt, nil +} + +// parseUpdateStatisticsStatementContinued parses UPDATE STATISTICS after consuming UPDATE. +func (p *Parser) parseUpdateStatisticsStatementContinued() (*ast.UpdateStatisticsStatement, error) { + // Consume STATISTICS + p.nextToken() + + stmt := &ast.UpdateStatisticsStatement{} + + // Parse table name + schemaObjectName, err := p.parseSchemaObjectName() + if err != nil { + return nil, err + } + stmt.SchemaObjectName = schemaObjectName + + // Parse optional SubElements (stat/index names) + // Can be either in parentheses: (c1, c2, c3) or a single identifier: st1 + if p.curTok.Type == TokenLParen { + p.nextToken() // consume ( + for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF { + ident := p.parseIdentifier() + stmt.SubElements = append(stmt.SubElements, ident) + if p.curTok.Type == TokenComma { + p.nextToken() + } + } + if p.curTok.Type == TokenRParen { + p.nextToken() // consume ) + } + } else if p.curTok.Type == TokenIdent { + // Single identifier without parentheses + ident := p.parseIdentifier() + stmt.SubElements = append(stmt.SubElements, ident) + } + + // Parse optional WITH clause + if p.curTok.Type == TokenWith { + p.nextToken() // consume WITH + + for p.curTok.Type != TokenSemicolon && p.curTok.Type != TokenEOF { + optionName := strings.ToUpper(p.curTok.Literal) + p.nextToken() // consume option name + + switch optionName { + case "ALL": + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.SimpleStatisticsOption{ + OptionKind: "All", + }) + case "FULLSCAN": + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.SimpleStatisticsOption{ + OptionKind: "FullScan", + }) + case "NORECOMPUTE": + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.SimpleStatisticsOption{ + OptionKind: "NoRecompute", + }) + case "COLUMNS": + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.SimpleStatisticsOption{ + OptionKind: "Columns", + }) + case "INDEX": + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.SimpleStatisticsOption{ + OptionKind: "Index", + }) + case "ROWCOUNT": + // Parse = value + if p.curTok.Type == TokenEquals { + p.nextToken() + } + val := p.curTok.Literal + p.nextToken() + // Use NumericLiteral for very large numbers, IntegerLiteral otherwise + var literal ast.ScalarExpression + if len(val) > 18 { // Numbers > 18 digits are likely > MaxInt64 + literal = &ast.NumericLiteral{LiteralType: "Numeric", Value: val} + } else { + literal = &ast.IntegerLiteral{LiteralType: "Integer", Value: val} + } + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.LiteralStatisticsOption{ + OptionKind: "RowCount", + Literal: literal, + }) + case "PAGECOUNT": + // Parse = value + if p.curTok.Type == TokenEquals { + p.nextToken() + } + val := p.curTok.Literal + p.nextToken() + // Use NumericLiteral for very large numbers, IntegerLiteral otherwise + var literal ast.ScalarExpression + if len(val) > 18 { // Numbers > 18 digits are likely > MaxInt64 + literal = &ast.NumericLiteral{LiteralType: "Numeric", Value: val} + } else { + literal = &ast.IntegerLiteral{LiteralType: "Integer", Value: val} + } + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.LiteralStatisticsOption{ + OptionKind: "PageCount", + Literal: literal, + }) + case "SAMPLE": + // Parse number PERCENT/ROWS + value, err := p.parseScalarExpression() + if err != nil { + return nil, err + } + mode := strings.ToUpper(p.curTok.Literal) + p.nextToken() // consume PERCENT or ROWS + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.LiteralStatisticsOption{ + OptionKind: "Sample" + strings.Title(strings.ToLower(mode)), + Literal: value, + }) + case "RESAMPLE": + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.ResampleStatisticsOption{ + OptionKind: "Resample", + }) + case "INCREMENTAL": + if p.curTok.Type == TokenEquals { + p.nextToken() + state := strings.ToUpper(p.curTok.Literal) + p.nextToken() + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.OnOffStatisticsOption{ + OptionKind: "Incremental", + OptionState: state, + }) + } else { + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.OnOffStatisticsOption{ + OptionKind: "Incremental", + OptionState: "On", + }) + } + default: + // Unknown option, skip + } + + if p.curTok.Type == TokenComma { + p.nextToken() + } else { + break + } + } + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + + return stmt, nil +} + diff --git a/parser/parse_statements.go b/parser/parse_statements.go index 344c1569..0dcf620c 100644 --- a/parser/parse_statements.go +++ b/parser/parse_statements.go @@ -13,6 +13,24 @@ func (p *Parser) parseDeclareVariableStatement() (ast.Statement, error) { // Consume DECLARE p.nextToken() + // Check if this is DECLARE cursor_name CURSOR (without @) + if p.curTok.Type == TokenIdent && !strings.HasPrefix(p.curTok.Literal, "@") { + // This might be DECLARE cursor_name CURSOR + cursorName := p.parseIdentifier() + + // Check for CURSOR keyword + if p.curTok.Type == TokenCursor { + return p.parseDeclareCursorStatementContinued(cursorName) + } + // Could also be old cursor syntax with options before CURSOR + kwd := strings.ToUpper(p.curTok.Literal) + if kwd == "INSENSITIVE" || kwd == "SCROLL" { + return p.parseDeclareCursorStatementContinued(cursorName) + } + // Not a cursor, error + return nil, fmt.Errorf("expected CURSOR after identifier in DECLARE, got %s", p.curTok.Literal) + } + // Parse variable name if p.curTok.Type != TokenIdent || !strings.HasPrefix(p.curTok.Literal, "@") { return nil, fmt.Errorf("expected variable name, got %s", p.curTok.Literal) @@ -4009,7 +4027,8 @@ func (p *Parser) parseCloseStatement() (ast.Statement, error) { return &ast.CloseMasterKeyStatement{}, nil } - return nil, fmt.Errorf("expected SYMMETRIC, ALL, or MASTER after CLOSE, got %s", p.curTok.Literal) + // Otherwise, it's CLOSE cursor_name + return p.parseCloseCursorStatement() } func (p *Parser) parseOpenStatement() (ast.Statement, error) { @@ -4062,7 +4081,8 @@ func (p *Parser) parseOpenStatement() (ast.Statement, error) { return stmt, nil } - return nil, fmt.Errorf("expected SYMMETRIC or MASTER after OPEN, got %s", p.curTok.Literal) + // Otherwise, it's OPEN cursor_name + return p.parseOpenCursorStatement() } func (p *Parser) parseCreateExternalStatement() (ast.Statement, error) { @@ -4511,6 +4531,17 @@ func (p *Parser) parseCreateDatabaseStatement() (ast.Statement, error) { stmt := &ast.CreateDatabaseStatement{ DatabaseName: p.parseIdentifier(), + AttachMode: "None", + } + + // Check for WITH clause + if p.curTok.Type == TokenWith { + p.nextToken() // consume WITH + opts, err := p.parseCreateDatabaseOptions() + if err != nil { + return nil, err + } + stmt.Options = opts } // Skip rest of statement @@ -4518,6 +4549,54 @@ func (p *Parser) parseCreateDatabaseStatement() (ast.Statement, error) { return stmt, nil } +func (p *Parser) parseCreateDatabaseOptions() ([]ast.CreateDatabaseOption, error) { + var options []ast.CreateDatabaseOption + + for { + optName := strings.ToUpper(p.curTok.Literal) + switch optName { + case "LEDGER": + p.nextToken() // consume LEDGER + if p.curTok.Type != TokenEquals { + return nil, fmt.Errorf("expected = after LEDGER, got %s", p.curTok.Literal) + } + p.nextToken() // consume = + state := strings.ToUpper(p.curTok.Literal) + p.nextToken() // consume ON/OFF + opt := &ast.OnOffDatabaseOption{ + OptionKind: "Ledger", + OptionState: capitalizeFirst(state), + } + options = append(options, opt) + + case "CATALOG_COLLATION": + p.nextToken() // consume CATALOG_COLLATION + if p.curTok.Type != TokenEquals { + return nil, fmt.Errorf("expected = after CATALOG_COLLATION, got %s", p.curTok.Literal) + } + p.nextToken() // consume = + opt := &ast.IdentifierDatabaseOption{ + OptionKind: "CatalogCollation", + Value: p.parseIdentifier(), + } + options = append(options, opt) + + default: + // Unknown option, return what we have + return options, nil + } + + // Check for comma separator + if p.curTok.Type == TokenComma { + p.nextToken() + } else { + break + } + } + + return options, nil +} + func (p *Parser) parseCreateLoginStatement() (*ast.CreateLoginStatement, error) { p.nextToken() // consume LOGIN @@ -4602,8 +4681,52 @@ func (p *Parser) parseCreateMessageTypeStatement() (*ast.CreateMessageTypeStatem Name: p.parseIdentifier(), } - // Skip rest of statement - p.skipToEndOfStatement() + // Optional AUTHORIZATION + if strings.ToUpper(p.curTok.Literal) == "AUTHORIZATION" { + p.nextToken() + stmt.Owner = p.parseIdentifier() + } + + // Optional VALIDATION + if strings.ToUpper(p.curTok.Literal) == "VALIDATION" { + p.nextToken() + if p.curTok.Type == TokenEquals { + p.nextToken() + } + valMethod := strings.ToUpper(p.curTok.Literal) + switch valMethod { + case "WELL_FORMED_XML": + stmt.ValidationMethod = "WellFormedXml" + p.nextToken() + case "NONE": + stmt.ValidationMethod = "None" + p.nextToken() + case "EMPTY": + stmt.ValidationMethod = "Empty" + p.nextToken() + case "VALID_XML": + stmt.ValidationMethod = "ValidXml" + p.nextToken() + // Expect WITH SCHEMA COLLECTION + if strings.ToUpper(p.curTok.Literal) == "WITH" { + p.nextToken() + if strings.ToUpper(p.curTok.Literal) == "SCHEMA" { + p.nextToken() + if strings.ToUpper(p.curTok.Literal) == "COLLECTION" { + p.nextToken() + schemaName, _ := p.parseSchemaObjectName() + stmt.XmlSchemaCollectionName = schemaName + } + } + } + } + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + return stmt, nil } @@ -4627,11 +4750,128 @@ func (p *Parser) parseCreateQueueStatement() (*ast.CreateQueueStatement, error) Name: name, } + // Check for WITH clause + if strings.ToUpper(p.curTok.Literal) == "WITH" { + p.nextToken() // consume WITH + opts, err := p.parseQueueOptions() + if err != nil { + return nil, err + } + stmt.QueueOptions = opts + } + // Skip rest of statement p.skipToEndOfStatement() return stmt, nil } +func (p *Parser) parseQueueOptions() ([]ast.QueueOption, error) { + var options []ast.QueueOption + + for { + optName := strings.ToUpper(p.curTok.Literal) + switch optName { + case "STATUS": + p.nextToken() // consume STATUS + if p.curTok.Type != TokenEquals { + return nil, fmt.Errorf("expected = after STATUS, got %s", p.curTok.Literal) + } + p.nextToken() // consume = + state := strings.ToUpper(p.curTok.Literal) + p.nextToken() // consume ON/OFF + opt := &ast.QueueStateOption{ + OptionState: capitalizeFirst(state), + OptionKind: "Status", + } + options = append(options, opt) + + case "RETENTION": + p.nextToken() // consume RETENTION + if p.curTok.Type != TokenEquals { + return nil, fmt.Errorf("expected = after RETENTION, got %s", p.curTok.Literal) + } + p.nextToken() // consume = + state := strings.ToUpper(p.curTok.Literal) + p.nextToken() // consume ON/OFF + opt := &ast.QueueStateOption{ + OptionState: capitalizeFirst(state), + OptionKind: "Retention", + } + options = append(options, opt) + + case "POISON_MESSAGE_HANDLING": + p.nextToken() // consume POISON_MESSAGE_HANDLING + if p.curTok.Type != TokenLParen { + return nil, fmt.Errorf("expected ( after POISON_MESSAGE_HANDLING, got %s", p.curTok.Literal) + } + p.nextToken() // consume ( + // Expect STATUS = ON/OFF + if strings.ToUpper(p.curTok.Literal) != "STATUS" { + return nil, fmt.Errorf("expected STATUS in POISON_MESSAGE_HANDLING, got %s", p.curTok.Literal) + } + p.nextToken() // consume STATUS + if p.curTok.Type != TokenEquals { + return nil, fmt.Errorf("expected = after STATUS in POISON_MESSAGE_HANDLING, got %s", p.curTok.Literal) + } + p.nextToken() // consume = + state := strings.ToUpper(p.curTok.Literal) + p.nextToken() // consume ON/OFF + if p.curTok.Type != TokenRParen { + return nil, fmt.Errorf("expected ) after POISON_MESSAGE_HANDLING status, got %s", p.curTok.Literal) + } + p.nextToken() // consume ) + opt := &ast.QueueStateOption{ + OptionState: capitalizeFirst(state), + OptionKind: "PoisonMessageHandlingStatus", + } + options = append(options, opt) + + case "ACTIVATION": + p.nextToken() // consume ACTIVATION + if p.curTok.Type != TokenLParen { + return nil, fmt.Errorf("expected ( after ACTIVATION, got %s", p.curTok.Literal) + } + p.nextToken() // consume ( + // Check for DROP or other activation options + if strings.ToUpper(p.curTok.Literal) == "DROP" { + p.nextToken() // consume DROP + if p.curTok.Type != TokenRParen { + return nil, fmt.Errorf("expected ) after ACTIVATION DROP, got %s", p.curTok.Literal) + } + p.nextToken() // consume ) + opt := &ast.QueueOptionSimple{ + OptionKind: "ActivationDrop", + } + options = append(options, opt) + } else { + // Skip to end of activation clause + depth := 1 + for depth > 0 && p.curTok.Type != TokenEOF { + if p.curTok.Type == TokenLParen { + depth++ + } else if p.curTok.Type == TokenRParen { + depth-- + } + p.nextToken() + } + } + + default: + // Unknown option, return what we have + return options, nil + } + + // Check for comma separator + if p.curTok.Type == TokenComma { + p.nextToken() + } else { + break + } + } + + return options, nil +} + func (p *Parser) parseCreateRouteStatement() (*ast.CreateRouteStatement, error) { p.nextToken() // consume ROUTE @@ -4678,11 +4918,96 @@ func (p *Parser) parseCreateApplicationRoleStatement() (*ast.CreateApplicationRo Name: p.parseIdentifier(), } - // Skip rest of statement - p.skipToEndOfStatement() + // Optional WITH clause + if p.curTok.Type == TokenWith { + p.nextToken() + opts, err := p.parseApplicationRoleOptions() + if err != nil { + return nil, err + } + stmt.ApplicationRoleOptions = opts + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + return stmt, nil } +func (p *Parser) parseApplicationRoleOptions() ([]*ast.ApplicationRoleOption, error) { + var options []*ast.ApplicationRoleOption + + for { + optionName := strings.ToUpper(p.curTok.Literal) + p.nextToken() + + // Expect = + if p.curTok.Type != TokenEquals { + return nil, fmt.Errorf("expected = after %s, got %s", optionName, p.curTok.Literal) + } + p.nextToken() + + opt := &ast.ApplicationRoleOption{} + + switch optionName { + case "PASSWORD": + opt.OptionKind = "Password" + // Parse string literal + if p.curTok.Type == TokenString { + val := p.curTok.Literal + // Strip quotes from string literal + if len(val) >= 2 && (val[0] == '\'' && val[len(val)-1] == '\'') { + val = val[1 : len(val)-1] + } + opt.Value = &ast.IdentifierOrValueExpression{ + Value: val, + ValueExpression: &ast.StringLiteral{ + Value: val, + LiteralType: "String", + }, + } + p.nextToken() + } + case "DEFAULT_SCHEMA": + opt.OptionKind = "DefaultSchema" + // Parse identifier + id := p.parseIdentifier() + opt.Value = &ast.IdentifierOrValueExpression{ + Value: id.Value, + Identifier: id, + } + case "NAME": + opt.OptionKind = "Name" + id := p.parseIdentifier() + opt.Value = &ast.IdentifierOrValueExpression{ + Value: id.Value, + Identifier: id, + } + case "LOGIN": + opt.OptionKind = "Login" + id := p.parseIdentifier() + opt.Value = &ast.IdentifierOrValueExpression{ + Value: id.Value, + Identifier: id, + } + default: + // Unknown option, skip + p.nextToken() + } + + options = append(options, opt) + + if p.curTok.Type != TokenComma { + break + } + p.nextToken() // consume comma + } + + return options, nil +} + func (p *Parser) parseCreateFulltextStatement() (ast.Statement, error) { p.nextToken() // consume FULLTEXT @@ -4741,8 +5066,99 @@ func (p *Parser) parseCreateStatisticsStatement() (*ast.CreateStatisticsStatemen Name: p.parseIdentifier(), } - // Skip rest of statement - p.skipToEndOfStatement() + // Parse ON table_name + if p.curTok.Type == TokenOn { + p.nextToken() // consume ON + tableName, err := p.parseSchemaObjectName() + if err != nil { + return nil, err + } + stmt.OnName = tableName + } + + // Parse columns in parentheses + if p.curTok.Type == TokenLParen { + p.nextToken() // consume ( + for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF { + // Parse column name + colRef, err := p.parseColumnReferenceOrFunctionCall() + if err != nil { + return nil, err + } + // Type assert to ColumnReferenceExpression + if cr, ok := colRef.(*ast.ColumnReferenceExpression); ok { + stmt.Columns = append(stmt.Columns, cr) + } + if p.curTok.Type == TokenComma { + p.nextToken() + } + } + if p.curTok.Type == TokenRParen { + p.nextToken() // consume ) + } + } + + // Parse optional WITH clause (reuse UPDATE STATISTICS options logic) + if p.curTok.Type == TokenWith { + p.nextToken() // consume WITH + + for p.curTok.Type != TokenSemicolon && p.curTok.Type != TokenEOF && p.curTok.Type != TokenWhere { + optionName := strings.ToUpper(p.curTok.Literal) + p.nextToken() // consume option name + + switch optionName { + case "FULLSCAN": + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.SimpleStatisticsOption{ + OptionKind: "FullScan", + }) + case "NORECOMPUTE": + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.SimpleStatisticsOption{ + OptionKind: "NoRecompute", + }) + case "SAMPLE": + // Parse number PERCENT/ROWS + value, err := p.parseScalarExpression() + if err != nil { + return nil, err + } + mode := strings.ToUpper(p.curTok.Literal) + p.nextToken() // consume PERCENT or ROWS + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.LiteralStatisticsOption{ + OptionKind: "Sample" + strings.Title(strings.ToLower(mode)), + Literal: value, + }) + case "INCREMENTAL": + if p.curTok.Type == TokenEquals { + p.nextToken() + state := strings.ToUpper(p.curTok.Literal) + p.nextToken() + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.OnOffStatisticsOption{ + OptionKind: "Incremental", + OptionState: state, + }) + } else { + stmt.StatisticsOptions = append(stmt.StatisticsOptions, &ast.OnOffStatisticsOption{ + OptionKind: "Incremental", + OptionState: "On", + }) + } + default: + // Unknown option, skip + } + + if p.curTok.Type == TokenComma { + p.nextToken() + } else { + break + } + } + } + + // Skip optional WHERE clause and rest of statement + if p.curTok.Type == TokenWhere || p.curTok.Type == TokenSemicolon { + p.skipToEndOfStatement() + } + return stmt, nil } @@ -4874,3 +5290,321 @@ func (p *Parser) parseRenameStatement() (*ast.RenameEntityStatement, error) { return stmt, nil } + +// parseCursorId parses a cursor identifier (optional GLOBAL, cursor name/variable). +func (p *Parser) parseCursorId() *ast.CursorId { + cursorId := &ast.CursorId{} + + // Check for GLOBAL keyword + if strings.ToUpper(p.curTok.Literal) == "GLOBAL" { + cursorId.IsGlobal = true + p.nextToken() + } + + // Parse cursor name or variable + cursorId.Name = &ast.IdentifierOrValueExpression{ + Value: p.curTok.Literal, + } + + // Check if it's a variable + if p.curTok.Type == TokenIdent && strings.HasPrefix(p.curTok.Literal, "@") { + cursorId.Name.ValueExpression = &ast.VariableReference{Name: p.curTok.Literal} + } else { + // Create identifier inline (same logic as parseIdentifier but without advancing) + literal := p.curTok.Literal + quoteType := "NotQuoted" + if len(literal) >= 2 && literal[0] == '[' && literal[len(literal)-1] == ']' { + quoteType = "SquareBracket" + literal = literal[1 : len(literal)-1] + } + cursorId.Name.Identifier = &ast.Identifier{ + Value: literal, + QuoteType: quoteType, + } + } + p.nextToken() + + return cursorId +} + +// parseOpenCursorStatement parses OPEN cursor_name. +func (p *Parser) parseOpenCursorStatement() (*ast.OpenCursorStatement, error) { + stmt := &ast.OpenCursorStatement{ + Cursor: p.parseCursorId(), + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + + return stmt, nil +} + +// parseCloseCursorStatement parses CLOSE cursor_name. +func (p *Parser) parseCloseCursorStatement() (*ast.CloseCursorStatement, error) { + stmt := &ast.CloseCursorStatement{ + Cursor: p.parseCursorId(), + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + + return stmt, nil +} + +// parseDeallocateCursorStatement parses DEALLOCATE cursor_name. +func (p *Parser) parseDeallocateCursorStatement() (*ast.DeallocateCursorStatement, error) { + // Already consumed DEALLOCATE + p.nextToken() + + // Check for optional CURSOR keyword + if p.curTok.Type == TokenCursor { + p.nextToken() + } + + stmt := &ast.DeallocateCursorStatement{ + Cursor: p.parseCursorId(), + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + + return stmt, nil +} + +// parseFetchCursorStatement parses FETCH ... FROM cursor_name. +func (p *Parser) parseFetchCursorStatement() (*ast.FetchCursorStatement, error) { + // Already consumed FETCH + p.nextToken() + + stmt := &ast.FetchCursorStatement{} + + // Check for fetch orientation + orientationKeyword := strings.ToUpper(p.curTok.Literal) + switch orientationKeyword { + case "NEXT": + stmt.FetchType = &ast.FetchType{Orientation: "Next"} + p.nextToken() + case "PRIOR": + stmt.FetchType = &ast.FetchType{Orientation: "Prior"} + p.nextToken() + case "FIRST": + stmt.FetchType = &ast.FetchType{Orientation: "First"} + p.nextToken() + case "LAST": + stmt.FetchType = &ast.FetchType{Orientation: "Last"} + p.nextToken() + case "ABSOLUTE": + p.nextToken() // consume ABSOLUTE + offset, err := p.parseScalarExpression() + if err != nil { + return nil, err + } + stmt.FetchType = &ast.FetchType{ + Orientation: "Absolute", + RowOffset: offset, + } + case "RELATIVE": + p.nextToken() // consume RELATIVE + offset, err := p.parseScalarExpression() + if err != nil { + return nil, err + } + stmt.FetchType = &ast.FetchType{ + Orientation: "Relative", + RowOffset: offset, + } + } + + // Check for FROM keyword + if p.curTok.Type == TokenFrom { + p.nextToken() + } + + // Parse cursor id + stmt.Cursor = p.parseCursorId() + + // Check for INTO clause + if p.curTok.Type == TokenInto { + p.nextToken() // consume INTO + for { + varRef, err := p.parseScalarExpression() + if err != nil { + return nil, err + } + stmt.IntoVariables = append(stmt.IntoVariables, varRef) + if p.curTok.Type != TokenComma { + break + } + p.nextToken() // consume comma + } + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + + return stmt, nil +} + +// parseDeclareCursorStatementContinued parses DECLARE cursor CURSOR ... after the cursor name. +func (p *Parser) parseDeclareCursorStatementContinued(cursorName *ast.Identifier) (*ast.DeclareCursorStatement, error) { + stmt := &ast.DeclareCursorStatement{ + Name: cursorName, + CursorDefinition: &ast.CursorDefinition{}, + } + + // Parse cursor options (INSENSITIVE, SCROLL, LOCAL, GLOBAL, FORWARD_ONLY, etc.) + for p.curTok.Type != TokenCursor && p.curTok.Type != TokenEOF && strings.ToUpper(p.curTok.Literal) != "FOR" { + kwd := strings.ToUpper(p.curTok.Literal) + switch kwd { + case "INSENSITIVE", "SCROLL", "LOCAL", "GLOBAL", "FORWARD_ONLY", "STATIC", + "KEYSET", "DYNAMIC", "FAST_FORWARD", "READ_ONLY", "SCROLL_LOCKS", + "OPTIMISTIC", "TYPE_WARNING": + stmt.CursorDefinition.Options = append(stmt.CursorDefinition.Options, &ast.CursorOption{ + OptionKind: toTitleCase(kwd), + }) + p.nextToken() + default: + break + } + if p.curTok.Type == TokenCursor || strings.ToUpper(p.curTok.Literal) == "FOR" { + break + } + } + + // Consume CURSOR keyword + if p.curTok.Type == TokenCursor { + p.nextToken() + } + + // Parse more options after CURSOR (for the new syntax) + for strings.ToUpper(p.curTok.Literal) != "FOR" && p.curTok.Type != TokenEOF { + kwd := strings.ToUpper(p.curTok.Literal) + switch kwd { + case "LOCAL", "GLOBAL", "FORWARD_ONLY", "SCROLL", "STATIC", "KEYSET", + "DYNAMIC", "FAST_FORWARD", "READ_ONLY", "SCROLL_LOCKS", "OPTIMISTIC", + "TYPE_WARNING": + stmt.CursorDefinition.Options = append(stmt.CursorDefinition.Options, &ast.CursorOption{ + OptionKind: toTitleCase(kwd), + }) + p.nextToken() + default: + break + } + if strings.ToUpper(p.curTok.Literal) == "FOR" { + break + } + } + + // Consume FOR keyword + if strings.ToUpper(p.curTok.Literal) == "FOR" { + p.nextToken() + } + + // Parse SELECT statement and extract its QueryExpression + selectStmt, err := p.parseSelectStatement() + if err != nil { + return nil, err + } + // CursorDefinition.Select is a QueryExpression, so we extract it from the SelectStatement + stmt.CursorDefinition.Select = selectStmt.QueryExpression + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + + return stmt, nil +} + +// toTitleCase converts underscore-separated names to TitleCase. +func toTitleCase(s string) string { + parts := strings.Split(strings.ToLower(s), "_") + for i, part := range parts { + if len(part) > 0 { + parts[i] = strings.ToUpper(part[0:1]) + part[1:] + } + } + return strings.Join(parts, "") +} + +// parseEnableDisableTriggerStatement parses ENABLE/DISABLE TRIGGER statements +func (p *Parser) parseEnableDisableTriggerStatement(enforcement string) (*ast.EnableDisableTriggerStatement, error) { + // Consume ENABLE or DISABLE + p.nextToken() + + // Expect TRIGGER + if strings.ToUpper(p.curTok.Literal) != "TRIGGER" { + return nil, fmt.Errorf("expected TRIGGER after %s, got %s", enforcement, p.curTok.Literal) + } + p.nextToken() + + stmt := &ast.EnableDisableTriggerStatement{ + TriggerEnforcement: enforcement, + } + + // Check for ALL + if strings.ToUpper(p.curTok.Literal) == "ALL" { + stmt.All = true + p.nextToken() + } else { + stmt.All = false + // Parse trigger names (comma-separated) + for { + name, err := p.parseSchemaObjectName() + if err != nil { + return nil, err + } + stmt.TriggerNames = append(stmt.TriggerNames, name) + + if p.curTok.Type != TokenComma { + break + } + p.nextToken() // consume comma + } + } + + // Expect ON + if p.curTok.Type != TokenOn { + return nil, fmt.Errorf("expected ON after trigger names, got %s", p.curTok.Literal) + } + p.nextToken() + + // Check for ALL SERVER or DATABASE or table name + stmt.TriggerObject = &ast.TriggerObject{} + + if strings.ToUpper(p.curTok.Literal) == "ALL" { + p.nextToken() + if strings.ToUpper(p.curTok.Literal) == "SERVER" { + stmt.TriggerObject.TriggerScope = "AllServer" + p.nextToken() + } else { + return nil, fmt.Errorf("expected SERVER after ALL, got %s", p.curTok.Literal) + } + } else if strings.ToUpper(p.curTok.Literal) == "DATABASE" { + stmt.TriggerObject.TriggerScope = "Database" + p.nextToken() + } else { + // Parse table name + tableName, err := p.parseSchemaObjectName() + if err != nil { + return nil, err + } + stmt.TriggerObject.Name = tableName + stmt.TriggerObject.TriggerScope = "Normal" + } + + // Skip optional semicolon + if p.curTok.Type == TokenSemicolon { + p.nextToken() + } + + return stmt, nil +} diff --git a/parser/parser.go b/parser/parser.go index c70bf527..eb86733d 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -92,7 +92,7 @@ func (p *Parser) parseStatement() (ast.Statement, error) { case TokenInsert: return p.parseInsertStatement() case TokenUpdate: - return p.parseUpdateStatement() + return p.parseUpdateOrUpdateStatisticsStatement() case TokenDelete: return p.parseDeleteStatement() case TokenDeclare: @@ -190,6 +190,22 @@ func (p *Parser) parseStatement() (ast.Statement, error) { if strings.ToUpper(p.curTok.Literal) == "RENAME" { return p.parseRenameStatement() } + // Check for FETCH cursor + if strings.ToUpper(p.curTok.Literal) == "FETCH" { + return p.parseFetchCursorStatement() + } + // Check for DEALLOCATE cursor + if strings.ToUpper(p.curTok.Literal) == "DEALLOCATE" { + return p.parseDeallocateCursorStatement() + } + // Check for ENABLE TRIGGER + if strings.ToUpper(p.curTok.Literal) == "ENABLE" { + return p.parseEnableDisableTriggerStatement("Enable") + } + // Check for DISABLE TRIGGER + if strings.ToUpper(p.curTok.Literal) == "DISABLE" { + return p.parseEnableDisableTriggerStatement("Disable") + } // Check for label (identifier followed by colon) return p.parseLabelOrError() default: diff --git a/parser/testdata/AlterDatabaseOptionsTests120/metadata.json b/parser/testdata/AlterDatabaseOptionsTests120/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/AlterDatabaseOptionsTests120/metadata.json +++ b/parser/testdata/AlterDatabaseOptionsTests120/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/AlterDatabaseStatementTests130/metadata.json b/parser/testdata/AlterDatabaseStatementTests130/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/AlterDatabaseStatementTests130/metadata.json +++ b/parser/testdata/AlterDatabaseStatementTests130/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/ApplicationRoleStatementTests/metadata.json b/parser/testdata/ApplicationRoleStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/ApplicationRoleStatementTests/metadata.json +++ b/parser/testdata/ApplicationRoleStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/Baselines100_QueueStatementTests100/metadata.json b/parser/testdata/Baselines100_QueueStatementTests100/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/Baselines100_QueueStatementTests100/metadata.json +++ b/parser/testdata/Baselines100_QueueStatementTests100/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/Baselines120_AlterDatabaseOptionsTests120/metadata.json b/parser/testdata/Baselines120_AlterDatabaseOptionsTests120/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/Baselines120_AlterDatabaseOptionsTests120/metadata.json +++ b/parser/testdata/Baselines120_AlterDatabaseOptionsTests120/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/Baselines130_AlterDatabaseStatementTests130/metadata.json b/parser/testdata/Baselines130_AlterDatabaseStatementTests130/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/Baselines130_AlterDatabaseStatementTests130/metadata.json +++ b/parser/testdata/Baselines130_AlterDatabaseStatementTests130/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/Baselines160_CreateDatabaseTests160/metadata.json b/parser/testdata/Baselines160_CreateDatabaseTests160/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/Baselines160_CreateDatabaseTests160/metadata.json +++ b/parser/testdata/Baselines160_CreateDatabaseTests160/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/Baselines90_ApplicationRoleStatementTests/metadata.json b/parser/testdata/Baselines90_ApplicationRoleStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/Baselines90_ApplicationRoleStatementTests/metadata.json +++ b/parser/testdata/Baselines90_ApplicationRoleStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/Baselines90_CreateMessageTypeStatementTests/metadata.json b/parser/testdata/Baselines90_CreateMessageTypeStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/Baselines90_CreateMessageTypeStatementTests/metadata.json +++ b/parser/testdata/Baselines90_CreateMessageTypeStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/Baselines90_EnableDisableTriggerStatementTests/metadata.json b/parser/testdata/Baselines90_EnableDisableTriggerStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/Baselines90_EnableDisableTriggerStatementTests/metadata.json +++ b/parser/testdata/Baselines90_EnableDisableTriggerStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/BaselinesCommon_BigIntRowCountPageCountTests/metadata.json b/parser/testdata/BaselinesCommon_BigIntRowCountPageCountTests/metadata.json index 49e9182b..e27d63a6 100644 --- a/parser/testdata/BaselinesCommon_BigIntRowCountPageCountTests/metadata.json +++ b/parser/testdata/BaselinesCommon_BigIntRowCountPageCountTests/metadata.json @@ -1 +1 @@ -{"skip": true} +{"skip": false} diff --git a/parser/testdata/BaselinesCommon_CreateStatisticsStatementTests/metadata.json b/parser/testdata/BaselinesCommon_CreateStatisticsStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/BaselinesCommon_CreateStatisticsStatementTests/metadata.json +++ b/parser/testdata/BaselinesCommon_CreateStatisticsStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/BaselinesCommon_CursorStatementsTests/metadata.json b/parser/testdata/BaselinesCommon_CursorStatementsTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/BaselinesCommon_CursorStatementsTests/metadata.json +++ b/parser/testdata/BaselinesCommon_CursorStatementsTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/BaselinesCommon_DeclareCursorStatementTests/metadata.json b/parser/testdata/BaselinesCommon_DeclareCursorStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/BaselinesCommon_DeclareCursorStatementTests/metadata.json +++ b/parser/testdata/BaselinesCommon_DeclareCursorStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/BaselinesCommon_UpdateStatisticsStatementTests/metadata.json b/parser/testdata/BaselinesCommon_UpdateStatisticsStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/BaselinesCommon_UpdateStatisticsStatementTests/metadata.json +++ b/parser/testdata/BaselinesCommon_UpdateStatisticsStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/BigIntRowCountPageCountTests/metadata.json b/parser/testdata/BigIntRowCountPageCountTests/metadata.json index 49e9182b..e27d63a6 100644 --- a/parser/testdata/BigIntRowCountPageCountTests/metadata.json +++ b/parser/testdata/BigIntRowCountPageCountTests/metadata.json @@ -1 +1 @@ -{"skip": true} +{"skip": false} diff --git a/parser/testdata/CreateDatabaseTests160/metadata.json b/parser/testdata/CreateDatabaseTests160/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/CreateDatabaseTests160/metadata.json +++ b/parser/testdata/CreateDatabaseTests160/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/CreateStatisticsStatementTests/metadata.json b/parser/testdata/CreateStatisticsStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/CreateStatisticsStatementTests/metadata.json +++ b/parser/testdata/CreateStatisticsStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/CursorStatementsTests/metadata.json b/parser/testdata/CursorStatementsTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/CursorStatementsTests/metadata.json +++ b/parser/testdata/CursorStatementsTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/DeclareCursorStatementTests/metadata.json b/parser/testdata/DeclareCursorStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/DeclareCursorStatementTests/metadata.json +++ b/parser/testdata/DeclareCursorStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/EnableDisableTriggerStatementTests/metadata.json b/parser/testdata/EnableDisableTriggerStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/EnableDisableTriggerStatementTests/metadata.json +++ b/parser/testdata/EnableDisableTriggerStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup1StatementTest/ast.json b/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup1StatementTest/ast.json index 7b89f03c..5c81d3bc 100644 --- a/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup1StatementTest/ast.json +++ b/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup1StatementTest/ast.json @@ -17,6 +17,7 @@ "Value": "fg1" }, "MakeDefault": false, + "UseCurrent": false, "UpdatabilityOption": "ReadOnly" } ] diff --git a/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup2StatementTest/ast.json b/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup2StatementTest/ast.json index 84d0adbd..66a45ef8 100644 --- a/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup2StatementTest/ast.json +++ b/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup2StatementTest/ast.json @@ -17,6 +17,7 @@ "Value": "fg1" }, "MakeDefault": false, + "UseCurrent": false, "NewFileGroupName": { "$type": "Identifier", "QuoteType": "NotQuoted", diff --git a/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup3StatementTest/ast.json b/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup3StatementTest/ast.json index 6e14f56d..a229990c 100644 --- a/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup3StatementTest/ast.json +++ b/parser/testdata/PhaseOne_AlterDatabaseModifyFilegroup3StatementTest/ast.json @@ -16,7 +16,8 @@ "QuoteType": "SquareBracket", "Value": "fg1" }, - "MakeDefault": true + "MakeDefault": true, + "UseCurrent": false } ] } diff --git a/parser/testdata/PhaseOne_AlterDatabaseRemoveFilegroupStatementTest/ast.json b/parser/testdata/PhaseOne_AlterDatabaseRemoveFilegroupStatementTest/ast.json index 6628b251..0d0f96f0 100644 --- a/parser/testdata/PhaseOne_AlterDatabaseRemoveFilegroupStatementTest/ast.json +++ b/parser/testdata/PhaseOne_AlterDatabaseRemoveFilegroupStatementTest/ast.json @@ -15,7 +15,8 @@ "$type": "Identifier", "QuoteType": "SquareBracket", "Value": "fg1" - } + }, + "UseCurrent": false } ] } diff --git a/parser/testdata/PhaseOne_CreateStatisticsStatementTest/ast.json b/parser/testdata/PhaseOne_CreateStatisticsStatementTest/ast.json index 0cfcb42c..56a82b29 100644 --- a/parser/testdata/PhaseOne_CreateStatisticsStatementTest/ast.json +++ b/parser/testdata/PhaseOne_CreateStatisticsStatementTest/ast.json @@ -10,6 +10,28 @@ "$type": "Identifier", "QuoteType": "NotQuoted", "Value": "stat1" + }, + "OnName": { + "$type": "SchemaObjectName", + "SchemaIdentifier": { + "$type": "Identifier", + "Value": "dbo", + "QuoteType": "NotQuoted" + }, + "BaseIdentifier": { + "$type": "Identifier", + "Value": "t1", + "QuoteType": "NotQuoted" + }, + "Count": 2, + "Identifiers": [ + { + "$ref": "Identifier" + }, + { + "$ref": "Identifier" + } + ] } } ] diff --git a/parser/testdata/QueueStatementTests100/metadata.json b/parser/testdata/QueueStatementTests100/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/QueueStatementTests100/metadata.json +++ b/parser/testdata/QueueStatementTests100/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/parser/testdata/UpdateStatisticsStatementTests/metadata.json b/parser/testdata/UpdateStatisticsStatementTests/metadata.json index 92f70877..e27d63a6 100644 --- a/parser/testdata/UpdateStatisticsStatementTests/metadata.json +++ b/parser/testdata/UpdateStatisticsStatementTests/metadata.json @@ -1 +1 @@ -{"skip": true} \ No newline at end of file +{"skip": false} diff --git a/skipped_tests_by_size.txt b/skipped_tests_by_size.txt index d466d6a1..0205bb8a 100644 --- a/skipped_tests_by_size.txt +++ b/skipped_tests_by_size.txt @@ -9,66 +9,32 @@ 129 CreateSchemaStatementErrorTests 133 GetTokenTypesTests 135 SimpleJsonObjectReturn160 -150 Baselines150_DeclareTableVariableTests150 164 Baselines100_RowsetsInSelectTests100 167 Baselines90_SetVariableStatementTests90 168 CreateTriggerStatementErrorTests -178 Baselines110_UseFederationTests110 -181 CreateAlterFederationStatementTestsAzure110 -183 Baselines100_QueueStatementTests100 -183 BaselinesCommon_DeclareTableStatementTests 183 CreateExternalFileFormatStatementTests160 -184 Baselines110_CreateAlterFederationStatementTestsAzure110 -184 BaselinesCommon_SaveTransactionStatementTests -194 AlterServerConfigurationStatementTests -196 Baselines100_AlterServerConfigurationStatementTests 203 Baselines160_CreateExternalFileFormatStatementTests160 208 BaselinesCommon_AlterProcedureStatementTests 208 RowsetsInSelectTests100 -213 Baselines150_AlterDatabaseScopedConfigClearProcCacheTests150 -213 DeclareTableVariableTests150 -218 AlterDatabaseScopedConfigClearProcCacheTests150 223 Baselines160_CreateExternalDataSourceStatementTests160 223 CreateExternalDataSourceStatementTests160 224 Baselines100_MiscTests100 224 Baselines150_AlterServerConfigurationExternalAuthenticationTests150 -224 BaselinesCommon_BigIntRowCountPageCountTests -224 BigIntRowCountPageCountTests 225 AlterServerConfigurationExternalAuthenticationTests150 -225 DeclareTableStatementTests 226 Baselines90_RowsetsInSelectTests90 231 Baselines90_ExecuteStatementTests90 -233 AlterResourceGovernorStatementTests -233 Baselines100_AlterResourceGovernorStatementTests -235 AlterFulltextCatalogStatementTests -235 UseFederationTests110 236 AlterTableStatementTests140_Azure 236 Baselines140_AlterTableStatementTests140_Azure 239 Baselines100_EventNotificationStatementTests100 -239 Baselines120_AlterDatabaseOptionsTests120 239 Baselines160_FuzzyStringMatchingTests160 240 RowsetsInSelectTests90 -241 Baselines150_AlterDatabaseScopedConfigForSecondaryClearProcCacheTests150 -241 Baselines90_AlterCertificateStatementTests -242 CryptographicProviderStatementTests 243 CreateWorkloadClassifierStatementSqlDwTests -244 AlterDatabaseScopedConfigForSecondaryClearProcCacheTests150 -244 QueueStatementTests100 -245 Baselines130_RenameStatementTests -245 Baselines90_AlterFulltextCatalogStatementTests 245 BaselinesCommon_SetVariableStatementTests -245 RenameStatementTests -247 SaveTransactionStatementTests 247 VectorTypeSecondParameterTests170 -249 AlterDatabaseOptionsTests120 250 CreateSpatialIndexStatementTests110 253 Baselines90_AlterTableDropTableElementStatementTests90 257 MiscTests100 -260 Baselines100_CryptographicProviderStatementTests 268 Baselines100_ExpressionTests100 -270 Baselines130_AlterDatabaseStatementTests130 -271 Baselines160_CreateDatabaseTests160 -271 CreateDatabaseTests160 272 AlterTableDropTableElementStatementTests90 273 Baselines110_ServerRoleStatementTests 275 BaselinesCommon_TSqlParserTestScript2 @@ -79,7 +45,6 @@ 278 BaselinesFabricDW_CloneTableTestsFabricDW 278 CloneTableTestsFabricDW 280 WhitespaceTests -284 AlterDatabaseStatementTests130 286 Baselines110_SendStatementTests 286 Baselines130_SecurityStatement130Tests 286 SecurityStatement130Tests @@ -88,13 +53,10 @@ 290 OptimizerHintsTests90 291 Baselines90_DumpLoadStatementTests 292 Baselines90_CreateTypeStatementTests -293 CursorStatementsTests -297 Baselines90_CreateMessageTypeStatementTests 298 ExecuteStatementTests90 301 AlterDatabaseManualCutoverTests170 301 Baselines170_AlterDatabaseManualCutoverTests170 302 CreateHekatonTriggerStatementTest -303 Baselines90_ApplicationRoleStatementTests 304 Baselines90_DumpLoadStatement90Tests 306 CreateTypeStatementTests 308 EventNotificationStatementTests100 @@ -104,19 +66,15 @@ 311 Baselines130_CreateHekatonTriggerStatementTest 312 Baselines110_SendStatementTests110 313 Baselines160_CreateFunctionStatementTests160 -314 BaselinesCommon_CursorStatementsTests 315 ServerAuditStatementTests110 -316 AlterCertificateStatementTests 317 AlterSequenceStatementTests 317 Baselines130_CreateWorkloadClassifierStatementSqlDwTests 320 BaselinesCommon_AlterFunctionStatementTests 322 Baselines90_CreateFulltextCatalogStatementTests -322 BaselinesCommon_CreateStatisticsStatementTests 324 BaselinesCommon_DeleteStatementTests 324 BaselinesCommon_IdentifierTests 325 Baselines130_CreateColumnStoreIndexTests130 325 Baselines150_AlterIndexStatementTests150 -328 EnableDisableTriggerStatementTests 329 Baselines90_InsertStatementTests90 332 SetVariableStatementTests90 334 AlterExternalLibrary140 @@ -131,7 +89,6 @@ 341 Baselines100_TableParametersTests 344 Baselines110_ServerAuditStatementTests110 345 CreateSequenceStatementTests -350 Baselines90_EnableDisableTriggerStatementTests 352 InsertStatementTests90 352 SelectStatementTests140 353 BeginEndStatementErrorTests @@ -151,7 +108,6 @@ 386 Baselines100_CTEStatementTests100 386 CreateDatabaseTests140 387 AlterFunctionJsonObjectTests160 -391 CreateStatisticsStatementTests 393 ServerRoleStatementTests 395 FromClauseTests120 398 Baselines90_AlterAsymmetricKeyStatementTests @@ -172,7 +128,6 @@ 423 Baselines90_ReceiveStatementTests 423 MultipleErrorTests 423 SetOffsetsAndOnOffSetTests -426 ApplicationRoleStatementTests 426 OpenSymmetricKeyStatementTests 427 SymmetricKeyStatementTests100 429 WithinGroupTests160 @@ -180,7 +135,6 @@ 430 ReceiveStatementTests 438 Baselines120_CreateAggregateStatementTests120 438 CreateAggregateStatementTests120 -438 DeclareCursorStatementTests 438 DumpLoadStatementTests 439 Baselines110_CreateSequenceStatementTests 442 ExpressionTests100 @@ -196,7 +150,6 @@ 455 ReturnStatementTests 455 TSqlParserTestScript1 456 Baselines90_AlterEndpointStatementTests -459 BaselinesCommon_DeclareCursorStatementTests 461 BaselinesCommon_ReturnStatementTests 462 Baselines160_AlterTableResumableTests160 463 BaselinesCommon_DropStatementsTests @@ -216,7 +169,6 @@ 483 Baselines90_AlterAssemblyStatementTests 484 TableTypeTests120 485 Baselines110_AlterSearchPropertyListStatementTests -488 BaselinesCommon_UpdateStatisticsStatementTests 492 AlterSearchPropertyListStatementTests 492 Baselines140_RestoreStatementTests140_Azure 493 RestoreStatementTests140_Azure @@ -290,7 +242,6 @@ 593 Baselines80_ParserModeTests 594 CreateIndexStatementTests110 594 EndConversationStatementTests -595 UpdateStatisticsStatementTests 596 SelectWithCollation 598 DeleteStatementTests 599 DbccStatementsTests