@@ -57,11 +57,107 @@ public class CypherASTBuilder extends Cypher25ParserBaseVisitor<Object> {
5757
5858 @ Override
5959 public CypherStatement visitStatement (final Cypher25Parser .StatementContext ctx ) {
60- // For now, focus on queryWithLocalDefinitions (the most common case)
61- if (ctx .queryWithLocalDefinitions () != null ) {
60+ if (ctx .queryWithLocalDefinitions () != null )
6261 return (CypherStatement ) visit (ctx .queryWithLocalDefinitions ());
62+ if (ctx .command () != null )
63+ return handleCommand (ctx .command ());
64+ throw new CommandParsingException ("Unsupported statement type" );
65+ }
66+
67+ private CypherDDLStatement handleCommand (final Cypher25Parser .CommandContext ctx ) {
68+ if (ctx .createCommand () != null )
69+ return handleCreateCommand (ctx .createCommand ());
70+ if (ctx .dropCommand () != null )
71+ return handleDropCommand (ctx .dropCommand ());
72+ throw new CommandParsingException ("Only constraint commands are currently supported" );
73+ }
74+
75+ private CypherDDLStatement handleCreateCommand (final Cypher25Parser .CreateCommandContext ctx ) {
76+ if (ctx .createConstraint () != null )
77+ return handleCreateConstraint (ctx .createConstraint ());
78+ throw new CommandParsingException ("Only CREATE CONSTRAINT is currently supported" );
79+ }
80+
81+ private CypherDDLStatement handleDropCommand (final Cypher25Parser .DropCommandContext ctx ) {
82+ if (ctx .dropConstraint () != null )
83+ return handleDropConstraint (ctx .dropConstraint ());
84+ throw new CommandParsingException ("Only DROP CONSTRAINT is currently supported" );
85+ }
86+
87+ private CypherDDLStatement handleCreateConstraint (final Cypher25Parser .CreateConstraintContext ctx ) {
88+ // Extract optional constraint name
89+ final String constraintName = ctx .symbolicNameOrStringParameter () != null
90+ ? stripBackticks (ctx .symbolicNameOrStringParameter ().getText ()) : null ;
91+
92+ // IF NOT EXISTS
93+ final boolean ifNotExists = ctx .IF () != null && ctx .NOT () != null && ctx .EXISTS () != null ;
94+
95+ // Extract label name and determine if it's a node or relationship constraint
96+ final boolean forRelationship ;
97+ final String labelName ;
98+ if (ctx .commandNodePattern () != null ) {
99+ forRelationship = false ;
100+ labelName = stripBackticks (ctx .commandNodePattern ().labelType ().symbolicNameString ().getText ());
101+ } else if (ctx .commandRelPattern () != null ) {
102+ forRelationship = true ;
103+ labelName = stripBackticks (ctx .commandRelPattern ().relType ().symbolicNameString ().getText ());
104+ } else {
105+ throw new CommandParsingException ("CREATE CONSTRAINT requires a node or relationship pattern" );
106+ }
107+
108+ // Extract property names from constraintType
109+ final Cypher25Parser .ConstraintTypeContext constraintType = ctx .constraintType ();
110+ final List <String > propertyNames = extractPropertyNames (constraintType );
111+
112+ // Determine constraint kind
113+ final CypherDDLStatement .ConstraintKind constraintKind ;
114+ if (constraintType instanceof Cypher25Parser .ConstraintIsUniqueContext )
115+ constraintKind = CypherDDLStatement .ConstraintKind .UNIQUE ;
116+ else if (constraintType instanceof Cypher25Parser .ConstraintIsNotNullContext )
117+ constraintKind = CypherDDLStatement .ConstraintKind .NOT_NULL ;
118+ else if (constraintType instanceof Cypher25Parser .ConstraintKeyContext )
119+ constraintKind = CypherDDLStatement .ConstraintKind .KEY ;
120+ else
121+ throw new CommandParsingException ("Unsupported constraint type: " + constraintType .getText ());
122+
123+ return new CypherDDLStatement (CypherDDLStatement .Kind .CREATE_CONSTRAINT , constraintKind ,
124+ constraintName , labelName , propertyNames , ifNotExists , false , forRelationship );
125+ }
126+
127+ private CypherDDLStatement handleDropConstraint (final Cypher25Parser .DropConstraintContext ctx ) {
128+ final String constraintName = stripBackticks (ctx .symbolicNameOrStringParameter ().getText ());
129+ final boolean ifExists = ctx .IF () != null && ctx .EXISTS () != null ;
130+ return new CypherDDLStatement (CypherDDLStatement .Kind .DROP_CONSTRAINT , null ,
131+ constraintName , null , null , false , ifExists , false );
132+ }
133+
134+ /**
135+ * Extracts property names from a constraintType context.
136+ * Handles both single property (p.id) and property list ((p.first, p.last)).
137+ */
138+ private List <String > extractPropertyNames (final Cypher25Parser .ConstraintTypeContext ctx ) {
139+ // propertyList() is defined on each specific subclass, not on the base ConstraintTypeContext
140+ final Cypher25Parser .PropertyListContext propList ;
141+ if (ctx instanceof Cypher25Parser .ConstraintIsUniqueContext )
142+ propList = ((Cypher25Parser .ConstraintIsUniqueContext ) ctx ).propertyList ();
143+ else if (ctx instanceof Cypher25Parser .ConstraintIsNotNullContext )
144+ propList = ((Cypher25Parser .ConstraintIsNotNullContext ) ctx ).propertyList ();
145+ else if (ctx instanceof Cypher25Parser .ConstraintKeyContext )
146+ propList = ((Cypher25Parser .ConstraintKeyContext ) ctx ).propertyList ();
147+ else
148+ throw new CommandParsingException ("Unsupported constraint type for property extraction" );
149+
150+ final List <String > names = new ArrayList <>();
151+ if (propList .enclosedPropertyList () != null ) {
152+ // Parenthesized list: (p.first, p.last)
153+ final Cypher25Parser .EnclosedPropertyListContext enclosed = propList .enclosedPropertyList ();
154+ for (final Cypher25Parser .PropertyContext prop : enclosed .property ())
155+ names .add (stripBackticks (prop .propertyKeyName ().getText ()));
156+ } else {
157+ // Single property: p.id
158+ names .add (stripBackticks (propList .property ().propertyKeyName ().getText ()));
63159 }
64- throw new CommandParsingException ( "Command statements not yet supported" ) ;
160+ return names ;
65161 }
66162
67163 @ Override
0 commit comments