55
66package org .opensearch .sql .executor ;
77
8- import java .io .IOException ;
9- import java .io .ObjectInputStream ;
8+ import com .google .common .hash .HashCode ;
9+ import java .io .ByteArrayInputStream ;
10+ import java .io .ByteArrayOutputStream ;
1011import java .util .ArrayList ;
1112import java .util .List ;
12- import java .util .stream . Collectors ;
13- import java .util .stream . Stream ;
13+ import java .util .zip . GZIPInputStream ;
14+ import java .util .zip . GZIPOutputStream ;
1415import lombok .Data ;
1516import lombok .RequiredArgsConstructor ;
17+ import lombok .SneakyThrows ;
1618import org .opensearch .sql .ast .tree .UnresolvedPlan ;
1719import org .opensearch .sql .expression .NamedExpression ;
18- import org .opensearch .sql .expression .ReferenceExpression ;
1920import org .opensearch .sql .expression .serialization .DefaultExpressionSerializer ;
2021import org .opensearch .sql .opensearch .executor .Cursor ;
2122import org .opensearch .sql .planner .PaginateOperator ;
2223import org .opensearch .sql .planner .physical .PhysicalPlan ;
23- import org .opensearch .sql .planner .physical .PhysicalPlanNodeVisitor ;
2424import org .opensearch .sql .planner .physical .ProjectOperator ;
2525import org .opensearch .sql .storage .StorageEngine ;
26- import org .opensearch .sql .storage .Table ;
2726import org .opensearch .sql .storage .TableScanOperator ;
28- import org .opensearch .sql .storage .read .TableScanBuilder ;
2927
3028@ RequiredArgsConstructor
3129public class PaginatedPlanCache {
@@ -39,7 +37,7 @@ public boolean canConvertToCursor(UnresolvedPlan plan) {
3937
4038 @ RequiredArgsConstructor
4139 @ Data
42- static class SeriazationContext {
40+ static class SerializationContext {
4341 private final PaginatedPlanCache cache ;
4442 }
4543
@@ -48,74 +46,104 @@ static class SeriazationContext {
4846 */
4947 public Cursor convertToCursor (PhysicalPlan plan ) {
5048 if (plan instanceof PaginateOperator ) {
51- var raw = CURSOR_PREFIX + plan .toCursor ();
49+ var cursor = plan .toCursor ();
50+ if (cursor == null || cursor .isEmpty ()) {
51+ return Cursor .None ;
52+ }
53+ var raw = CURSOR_PREFIX + compress (cursor );
5254 return new Cursor (raw .getBytes ());
5355 } else {
5456 return Cursor .None ;
5557 }
5658 }
5759
60+ @ SneakyThrows
61+ public static String compress (String str ) {
62+ if (str == null || str .length () == 0 ) {
63+ return null ;
64+ }
65+
66+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
67+ GZIPOutputStream gzip = new GZIPOutputStream (out );
68+ gzip .write (str .getBytes ());
69+ gzip .close ();
70+ return HashCode .fromBytes (out .toByteArray ()).toString ();
71+ }
72+
73+ @ SneakyThrows
74+ public static String decompress (String input ) {
75+ if (input == null || input .length () == 0 ) {
76+ return null ;
77+ }
78+ GZIPInputStream gzip = new GZIPInputStream (new ByteArrayInputStream (
79+ HashCode .fromString (input ).asBytes ()));
80+ return new String (gzip .readAllBytes ());
81+ }
82+
83+ /**
84+ * Parse `NamedExpression`s from cursor.
85+ * @param listToFill List to fill with data.
86+ * @param cursor Cursor to parse.
87+ * @return Remaining part of the cursor.
88+ */
89+ private String parseNamedExpressions (List <NamedExpression > listToFill , String cursor ) {
90+ var serializer = new DefaultExpressionSerializer ();
91+ while (!cursor .startsWith (")" ) && !cursor .startsWith ("(" )) {
92+ listToFill .add ((NamedExpression )
93+ serializer .deserialize (cursor .substring (0 ,
94+ Math .min (cursor .indexOf (',' ), cursor .indexOf (')' )))));
95+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
96+ }
97+ return cursor ;
98+ }
99+
58100 /**
59101 * Converts a cursor to a physical plan tree.
60102 */
61103 public PhysicalPlan convertToPlan (String cursor ) {
62104 if (cursor .startsWith (CURSOR_PREFIX )) {
63105 try {
64- String expression = cursor .substring (CURSOR_PREFIX .length ());
65-
66- // TODO Parse expression and initialize variables below.
67- // storageEngine needs to create the TableScanOperator.
106+ cursor = cursor .substring (CURSOR_PREFIX .length ());
107+ cursor = decompress (cursor );
68108
69109 // TODO Parse with ANTLR or serialize as JSON/XML
70- if (!expression .startsWith ("(Paginate," )) {
110+ if (!cursor .startsWith ("(Paginate," )) {
71111 throw new UnsupportedOperationException ("Unsupported cursor" );
72112 }
73- expression = expression .substring (expression .indexOf (',' ) + 1 );
74- int currentPageIndex = Integer .parseInt (expression , 0 , expression .indexOf (',' ), 10 );
113+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
114+ int currentPageIndex = Integer .parseInt (cursor , 0 , cursor .indexOf (',' ), 10 );
75115
76- expression = expression .substring (expression .indexOf (',' ) + 1 );
77- int pageSize = Integer .parseInt (expression , 0 , expression .indexOf (',' ), 10 );
116+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
117+ int pageSize = Integer .parseInt (cursor , 0 , cursor .indexOf (',' ), 10 );
78118
79- expression = expression .substring (expression .indexOf (',' ) + 1 );
80- if (!expression .startsWith ("(Project," )) {
119+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
120+ if (!cursor .startsWith ("(Project," )) {
81121 throw new UnsupportedOperationException ("Unsupported cursor" );
82122 }
83- expression = expression .substring (expression .indexOf (',' ) + 1 );
84- if (!expression .startsWith ("(namedParseExpressions," )) {
123+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
124+ if (!cursor .startsWith ("(namedParseExpressions," )) {
85125 throw new UnsupportedOperationException ("Unsupported cursor" );
86126 }
87- expression = expression .substring (expression .indexOf (',' ) + 1 );
88- var serializer = new DefaultExpressionSerializer ();
89- // TODO parse npe
90- List <NamedExpression > namedParseExpressions = List .of ();
91127
92- expression = expression .substring (expression .indexOf (',' ) + 1 );
128+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
129+ List <NamedExpression > namedParseExpressions = new ArrayList <>();
130+ cursor = parseNamedExpressions (namedParseExpressions , cursor );
131+
132+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
93133 List <NamedExpression > projectList = new ArrayList <>();
94- if (!expression .startsWith ("(projectList," )) {
134+ if (!cursor .startsWith ("(projectList," )) {
95135 throw new UnsupportedOperationException ("Unsupported cursor" );
96136 }
97- expression = expression .substring (expression .indexOf (',' ) + 1 );
98- while (expression .startsWith ("(named," )) {
99- expression = expression .substring (expression .indexOf (',' ) + 1 );
100- var name = expression .substring (0 , expression .indexOf (',' ));
101- expression = expression .substring (expression .indexOf (',' ) + 1 );
102- var alias = expression .substring (0 , expression .indexOf (',' ));
103- if (alias .isEmpty ()) {
104- alias = null ;
105- }
106- expression = expression .substring (expression .indexOf (',' ) + 1 );
107- projectList .add (new NamedExpression (name ,
108- serializer .deserialize (expression .substring (0 , expression .indexOf (')' ))), alias ));
109- expression = expression .substring (expression .indexOf (',' ) + 1 );
110- }
137+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
138+ cursor = parseNamedExpressions (projectList , cursor );
111139
112- if (!expression .startsWith ("(OpenSearchPagedIndexScan," )) {
140+ if (!cursor .startsWith ("(OpenSearchPagedIndexScan," )) {
113141 throw new UnsupportedOperationException ("Unsupported cursor" );
114142 }
115- expression = expression .substring (expression .indexOf (',' ) + 1 );
116- var indexName = expression .substring (0 , expression .indexOf (',' ));
117- expression = expression .substring (expression .indexOf (',' ) + 1 );
118- var scrollId = expression .substring (0 , expression .indexOf (')' ));
143+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
144+ var indexName = cursor .substring (0 , cursor .indexOf (',' ));
145+ cursor = cursor .substring (cursor .indexOf (',' ) + 1 );
146+ var scrollId = cursor .substring (0 , cursor .indexOf (')' ));
119147 TableScanOperator scan = storageEngine .getTableScan (indexName , scrollId );
120148
121149 return new PaginateOperator (new ProjectOperator (scan , projectList , namedParseExpressions ),
0 commit comments