1515import org .elasticsearch .license .internal .XPackLicenseStatus ;
1616import org .elasticsearch .test .ESTestCase ;
1717import org .elasticsearch .xpack .esql .EsqlTestUtils ;
18+ import org .elasticsearch .xpack .esql .LicenseAware ;
1819import org .elasticsearch .xpack .esql .VerificationException ;
1920import org .elasticsearch .xpack .esql .analysis .Analyzer ;
2021import org .elasticsearch .xpack .esql .analysis .AnalyzerContext ;
2526import org .elasticsearch .xpack .esql .core .tree .Source ;
2627import org .elasticsearch .xpack .esql .core .type .DataType ;
2728import org .elasticsearch .xpack .esql .parser .EsqlParser ;
29+ import org .elasticsearch .xpack .esql .plan .logical .Limit ;
2830import org .elasticsearch .xpack .esql .plan .logical .LogicalPlan ;
2931import org .elasticsearch .xpack .esql .stats .Metrics ;
3032
3133import java .util .List ;
34+ import java .util .Objects ;
3235
3336import static org .elasticsearch .xpack .esql .analysis .AnalyzerTestUtils .analyzerDefaultMapping ;
3437import static org .elasticsearch .xpack .esql .analysis .AnalyzerTestUtils .defaultEnrichResolution ;
@@ -44,33 +47,42 @@ public void testLicense() {
4447 final LicensedFeature functionLicenseFeature = random ().nextBoolean ()
4548 ? LicensedFeature .momentary ("test" , "license" , functionLicense )
4649 : LicensedFeature .persistent ("test" , "license" , functionLicense );
47- final EsqlFunctionRegistry .FunctionBuilder builder = (source , expression , cfg ) -> {
48- final LicensedFunction licensedFunction = new LicensedFunction (source );
49- licensedFunction .setLicensedFeature (functionLicenseFeature );
50- return licensedFunction ;
51- };
5250 for (License .OperationMode operationMode : License .OperationMode .values ()) {
5351 if (License .OperationMode .TRIAL != operationMode && License .OperationMode .compare (operationMode , functionLicense ) < 0 ) {
5452 // non-compliant license
55- final VerificationException ex = expectThrows (VerificationException .class , () -> analyze (builder , operationMode ));
56- assertThat (ex .getMessage (), containsString ("current license is non-compliant for function [license()]" ));
53+ final VerificationException ex = expectThrows (
54+ VerificationException .class ,
55+ () -> analyze (operationMode , functionLicenseFeature )
56+ );
57+ assertThat (ex .getMessage (), containsString ("current license is non-compliant for [license()]" ));
58+ assertThat (ex .getMessage (), containsString ("current license is non-compliant for [LicensedLimit]" ));
5759 } else {
5860 // compliant license
59- assertNotNull (analyze (builder , operationMode ));
61+ assertNotNull (analyze (operationMode , functionLicenseFeature ));
6062 }
6163 }
6264 }
6365 }
6466
65- private LogicalPlan analyze (EsqlFunctionRegistry .FunctionBuilder builder , License .OperationMode operationMode ) {
67+ private LogicalPlan analyze (License .OperationMode operationMode , LicensedFeature functionLicenseFeature ) {
68+ final EsqlFunctionRegistry .FunctionBuilder builder = (source , expression , cfg ) -> new LicensedFunction (
69+ source ,
70+ functionLicenseFeature
71+ );
6672 final FunctionDefinition def = EsqlFunctionRegistry .def (LicensedFunction .class , builder , "license" );
6773 final EsqlFunctionRegistry registry = new EsqlFunctionRegistry (def ) {
6874 @ Override
6975 public EsqlFunctionRegistry snapshotRegistry () {
7076 return this ;
7177 }
7278 };
73- return analyzer (registry , operationMode ).analyze (parser .createStatement (esql ));
79+
80+ var plan = parser .createStatement (esql );
81+ plan = plan .transformDown (
82+ Limit .class ,
83+ l -> Objects .equals (l .limit ().fold (), 10 ) ? new LicensedLimit (l .source (), l .limit (), l .child (), functionLicenseFeature ) : l
84+ );
85+ return analyzer (registry , operationMode ).analyze (plan );
7486 }
7587
7688 private static Analyzer analyzer (EsqlFunctionRegistry registry , License .OperationMode operationMode ) {
@@ -88,25 +100,18 @@ private static XPackLicenseState getLicenseState(License.OperationMode operation
88100
89101 // It needs to be public because we run validation on it via reflection in org.elasticsearch.xpack.esql.tree.EsqlNodeSubclassTests.
90102 // This test prevents to add the license as constructor parameter too.
91- public static class LicensedFunction extends Function {
103+ public static class LicensedFunction extends Function implements LicenseAware {
92104
93- private LicensedFeature licensedFeature ;
105+ private final LicensedFeature licensedFeature ;
94106
95- public LicensedFunction (Source source ) {
107+ public LicensedFunction (Source source , LicensedFeature licensedFeature ) {
96108 super (source , List .of ());
97- }
98-
99- void setLicensedFeature (LicensedFeature licensedFeature ) {
100109 this .licensedFeature = licensedFeature ;
101110 }
102111
103112 @ Override
104- public boolean checkLicense (XPackLicenseState state ) {
105- if (licensedFeature instanceof LicensedFeature .Momentary momentary ) {
106- return momentary .check (state );
107- } else {
108- return licensedFeature .checkWithoutTracking (state );
109- }
113+ public boolean licenseCheck (XPackLicenseState state ) {
114+ return checkLicense (state , licensedFeature );
110115 }
111116
112117 @ Override
@@ -121,7 +126,7 @@ public Expression replaceChildren(List<Expression> newChildren) {
121126
122127 @ Override
123128 protected NodeInfo <? extends Expression > info () {
124- return NodeInfo .create (this );
129+ return NodeInfo .create (this , LicensedFunction :: new , licensedFeature );
125130 }
126131
127132 @ Override
@@ -135,4 +140,39 @@ public void writeTo(StreamOutput out) {
135140 }
136141 }
137142
143+ public static class LicensedLimit extends Limit implements LicenseAware {
144+
145+ private final LicensedFeature licensedFeature ;
146+
147+ public LicensedLimit (Source source , Expression limit , LogicalPlan child , LicensedFeature licensedFeature ) {
148+ super (source , limit , child );
149+ this .licensedFeature = licensedFeature ;
150+ }
151+
152+ @ Override
153+ public boolean licenseCheck (XPackLicenseState state ) {
154+ return checkLicense (state , licensedFeature );
155+ }
156+
157+ @ Override
158+ public Limit replaceChild (LogicalPlan newChild ) {
159+ return new LicensedLimit (source (), limit (), newChild , licensedFeature );
160+ }
161+
162+ @ Override
163+ protected NodeInfo <Limit > info () {
164+ return NodeInfo .create (this , LicensedLimit ::new , limit (), child (), licensedFeature );
165+ }
166+
167+ @ Override
168+ public String sourceText () {
169+ return "LicensedLimit" ;
170+ }
171+ }
172+
173+ private static boolean checkLicense (XPackLicenseState state , LicensedFeature licensedFeature ) {
174+ return licensedFeature instanceof LicensedFeature .Momentary momentary
175+ ? momentary .check (state )
176+ : licensedFeature .checkWithoutTracking (state );
177+ }
138178}
0 commit comments