1 /*
2  * Entity - Entity is an object-relational mapping tool for the D programming language. Referring to the design idea of JPA.
3  *
4  * Copyright (C) 2015-2018  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs.cn
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.entity.eql.EqlParse;
13 
14 import hunt.entity.eql.EqlInfo;
15 import hunt.entity;
16 import hunt.entity.EntityMetaInfo;
17 import hunt.sql;
18 
19 import hunt.logging;
20 import hunt.collection;
21 import hunt.math;
22 import hunt.Number;
23 import hunt.String;
24 import hunt.Integer;
25 import hunt.Long;
26 import hunt.Double;
27 import hunt.Float;
28 import hunt.Short;
29 import hunt.Byte;
30 import hunt.Boolean;
31 import hunt.database;
32 import hunt.Nullable;
33 
34 import std.algorithm.sorting;
35 import std.format;
36 import std.regex;
37 import std.string;
38 
39 void eql_throw(string type, string message) {
40     throw new Exception("[EQL PARSE EXCEPTION. " ~ type ~ "] " ~ message);
41 }
42 
43 /**
44  * 
45  */
46 private class EqlSubstitutionContext {
47     // EqlObject[string] eqlObjs;
48     // The key is the name of a model.
49     EntityFieldInfo[string][string] tableFields;
50 
51     string[string] modelTableMap;
52     string[string] aliasModelMap;
53 
54     string getTableByModel(string name, string defaultValue) {
55         auto itemPtr = name in modelTableMap;
56         if(itemPtr is null) {
57             version (HUNT_ENTITY_DEBUG) warningf("Can't find the table name for a mode: %s", name);
58             return defaultValue;
59         } else {
60             return *itemPtr;
61         }
62     }
63 
64     string getModelByAlias(string name, string defaultValue) {
65         auto itemPtr = name in aliasModelMap;
66         if(itemPtr is null) {
67             version (HUNT_ENTITY_DEBUG) warningf("Can't find the model name for an alias: %s", name);
68             return defaultValue;
69         } else {
70             return *itemPtr;
71         }
72     }
73 }
74 
75 /**
76  * 
77  */
78 class EqlParse {
79     alias EntityField = EntityFieldInfo[string];
80     private string _eql;
81     private string _parsedEql;
82     private string _dbtype;
83     private ExportTableAliasVisitor _aliasVistor; //Table and alias
84     private SchemaStatVisitor _schemaVistor;
85     private List!SQLStatement _stmtList;
86     private string[string] _clsNameToTbName;
87 
88     private EntityField[string] _tableFields; //Class name and field name
89     private EqlObject[string] _eqlObj;
90     public string[string] _objType;
91     private Object[string] _joinConds;
92 
93     private Object[int] _params;
94     private Object[string] _parameters;
95 
96     private EntityMetaInfo _entityInfo;
97     private FormatOption _formatOption;
98     private string _nativeSql;
99 
100     this(string eql, ref EntityMetaInfo entityInfo, string dbtype = "mysql")
101     {
102         _entityInfo = entityInfo;
103         _parsedEql = _eql = eql;
104         _dbtype = dbtype;
105         _aliasVistor = new ExportTableAliasVisitor();
106         _schemaVistor = SQLUtils.createSchemaStatVisitor(_dbtype);
107 
108         _formatOption = new FormatOption(true, true);
109         _formatOption.config(VisitorFeature.OutputQuotedIdentifier, true);
110     }
111 
112     FormatOption formatOption() {
113         return _formatOption;
114     }
115 
116     void formatOption(FormatOption value) {
117         _formatOption = value;
118     }
119 
120     string getEql()
121     {
122         return _eql;
123     }
124 
125     string getDBType()
126     {
127         return _dbtype;
128     }
129 
130     void setParsedEql(string parsedEql)
131     {
132         _parsedEql = parsedEql;
133     }
134 
135     string getParsedEql()
136     {
137         return _parsedEql;
138     }
139 
140     public void parse()
141     {
142         try
143         {
144             // version(HUNT_DEBUG)logInfo("-----> :",_parsedEql);
145             _stmtList = SQLUtils.parseStatements(_parsedEql, _dbtype);
146 
147             foreach (stmt; _stmtList)
148             {
149                 stmt.accept(_aliasVistor);
150                 stmt.accept(_schemaVistor);
151             }
152 
153             if (_stmtList.size == 0)
154                 eql_throw("Statement", " statement error : " ~ _parsedEql);
155 
156             init();
157         }
158         catch (Exception e)
159         {
160             throw e;
161         }
162 
163     }
164 
165     private void init()
166     {
167         version(HUNT_ENTITY_DEBUG) {
168             tracef("_objType: %s", _objType);
169         }
170 
171         _nativeSql = "";
172         Map!(string, SQLTableSource) aliasMap = _aliasVistor.getAliasMap();
173         foreach (string objName, SQLTableSource v; aliasMap)
174         {
175             string clsName;
176             SQLExpr expr = (cast(SQLExprTableSource) v).getExpr();
177             if (cast(SQLIdentifierExpr) expr !is null)
178             {
179                 clsName = (cast(SQLIdentifierExpr) expr).getName();
180             }
181             else if (cast(SQLPropertyExpr) expr !is null)
182             {
183                 // clsName = (cast(SQLPropertyExpr)expr);
184                 clsName = _objType.get(convertExprAlias(cast(SQLPropertyExpr) expr), null);
185             } else {
186                 version(HUNT_ENTITY_DEBUG) {
187                     warningf("Unhandled expression: %s", typeid(cast(Object)expr));
188                 }
189             }
190 
191             if(clsName.empty() || objName.empty()) {
192                 version(HUNT_ENTITY_DEBUG) {
193                     warningf("clsName: %s, objName: %s", clsName, objName);
194                 }
195                 continue;
196             }
197 
198             auto obj = new EqlObject(objName, clsName);
199             _eqlObj[objName] = obj;
200         }
201 
202         version (HUNT_ENTITY_DEBUG) {
203             trace(_clsNameToTbName);
204         }
205 
206         foreach (string objName, EqlObject obj; _eqlObj)
207         {
208             string className = obj.className();
209             if(className.empty()) {
210                 warningf("className is empty for %s", objName);
211                 continue;
212             }
213             // assert(!className.empty());
214 
215             string tableName = _clsNameToTbName.get(className, null);
216             if (tableName.empty()) {
217                 eql_throw(className, "table is not found");
218             }
219             obj.setTableName(tableName);
220         }
221 
222         // logDebug("aliasMap : %s".format(aliasMap));
223 
224         // logDebug("cls name : %s".format(_clsNameToTbName));
225 
226         // logDebug("EQL OBJS : %s".format(_eqlObj));
227 
228         // logDebug("EQL Conds : %s".format(_joinConds));
229 
230         // logDebug("EQL objType : %s".format(_objType));
231 
232         if (cast(SQLSelectStatement)(_stmtList.get(0)) !is null)
233         {
234             // logDebug("EQL do_select_parse");
235             doSelectParse();
236         }
237         else if (cast(SQLUpdateStatement)(_stmtList.get(0)) !is null)
238         {
239             // logDebug("EQL do_update_parse");
240             doUpdateParse();
241         }
242         else if (cast(SQLDeleteStatement)(_stmtList.get(0)) !is null)
243         {
244             // logDebug("EQL do_delete_parse");
245             doDeleteParse();
246         }
247         else if (cast(SQLInsertStatement)(_stmtList.get(0)) !is null)
248         {
249             version (HUNT_ENTITY_DEBUG_MORE)
250                 logDebug("EQL do_insert_parse");
251             doInsertParse();
252         }
253         else
254         {
255             eql_throw("Statement",
256                     " unknown sql statement : " ~ typeid(cast(Object)(_stmtList.get(0))).toString);
257         }
258 
259         // logDebug("init eql : %s".format(_parsedEql));
260     }
261 
262     private void doSelectParse()
263     {
264         auto queryBlock = (cast(SQLSelectStatement)(_stmtList.get(0))).getSelect().getQueryBlock();
265         auto select_copy = queryBlock.clone();
266         select_copy.getSelectList().clear();
267         /// select item
268         foreach (selectItem; queryBlock.getSelectList())
269         {
270             auto expr = selectItem.getExpr();
271             
272             Object oo = cast(Object)expr;
273             // version(HUNT_DEBUG) infof("Expr: %s, item: %s", typeid(oo).name, SQLUtils.toSQLString(selectItem));
274             version(HUNT_ENTITY_DEBUG) infof("Expr: %s", typeid(oo).name);
275 
276             if (cast(SQLIdentifierExpr) expr !is null)
277             {
278                 auto eqlObj = _eqlObj.get((cast(SQLIdentifierExpr) expr).getName(), null);
279                 if (eqlObj !is null)
280                 {
281                     auto clsName = eqlObj.className();
282                     EntityFieldInfo[string] fields = _tableFields.get(clsName, null);
283 
284                     if (fields !is null)
285                     {
286                         foreach (string classMember, EntityFieldInfo fieldInfo; fields)
287                         {
288                             if(fieldInfo.isAggregateType()) {
289                                 // ignore all the aggregative members;
290                                 version(HUNT_DEBUG) {
291                                     infof("Aggregative member ignored: %s", classMember);
292                                 }
293 
294                                 continue;
295                             }
296 
297                             if (!(clsName ~ "." ~ classMember in _objType)) /// ordinary member
298                             {
299                                 string columnAlias = selectItem.getAlias();
300 
301                                 version(HUNT_ENTITY_DEBUG) {
302                                     tracef("columnAlias: [%s], SelectColumn: [%s], fullColumn: [%s]", 
303                                         columnAlias, fieldInfo.getSelectColumn(), fieldInfo.getFullColumn());
304                                 }
305 
306                                 // SQLIdentifierExpr identifierExpr = new SQLIdentifierExpr(fieldInfo.getFullColumn());                                    
307 
308                                 SQLIdentifierExpr identifierExpr = new SQLIdentifierExpr(columnAlias.empty()
309                                         ? fieldInfo.getSelectColumn()
310                                         : fieldInfo.getFullColumn());
311 
312                                 select_copy.addSelectItem(identifierExpr, columnAlias);
313                                 // logDebug("sql replace : (%s ,%s) ".format(clsName ~ "." ~ classMember,clsName ~ "." ~ fieldInfo.getSelectColumn()));
314                             }
315                         }
316                     }
317                 }
318             }
319             else if (cast(SQLPropertyExpr) expr !is null)
320             {
321                 auto eqlObj = _eqlObj.get((cast(SQLPropertyExpr) expr).getOwnernName(), null);
322                 auto currentFieldName = (cast(SQLPropertyExpr) expr).getName();
323                 if (eqlObj !is null)
324                 {
325                     bool isHandled = false;
326                     auto fields = _tableFields.get(eqlObj.className(), null);
327                     if (fields !is null)
328                     {
329                         foreach (string classMember, EntityFieldInfo fieldInfo; fields) {
330                             string columnName = fieldInfo.getColumnName();
331                             version(HUNT_ENTITY_DEBUG_MORE) {
332                                 tracef("classMember: %s, columnName: %s, currentField: %s", 
333                                     classMember, columnName, currentFieldName);
334                             }
335                             isHandled = classMember == currentFieldName || columnName == currentFieldName;
336                             if (isHandled)
337                             {
338                                 select_copy.addSelectItem(new SQLIdentifierExpr(selectItem.getAlias() is null
339                                         ? fieldInfo.getSelectColumn()
340                                         : fieldInfo.getFullColumn()), selectItem.getAlias());
341                                 break;
342                             }
343                             // logDebug("sql replace : (%s ,%s) ".format(k ~ "." ~ classMember,k ~ "." ~ fieldInfo.getColumnName()));
344                         }
345                     }
346 
347                     if(!isHandled) {
348                         string msg = format("Found a undefined memeber [%s] in class [%s]", 
349                             currentFieldName, eqlObj.className());
350                         warningf(msg);
351 
352                         // TODO: Tasks pending completion -@zhangxueping at 2020-10-20T14:32:17+08:00
353                         // 
354                         // throw new EntityException(msg);
355                     }                    
356                 }
357                 else
358                 {
359                     eql_throw("Statement",
360                     " undefined sql object  '%s' in '%s': ".format((cast(SQLPropertyExpr) expr).getOwnernName(),SQLUtils.toSQLString(expr)));
361                 }
362             }
363             else if (cast(SQLAggregateExpr) expr !is null)
364             {
365                 SQLAggregateExpr aggreExpr = cast(SQLAggregateExpr) expr;
366                 List!SQLExpr newArgs = new ArrayList!SQLExpr();
367                 foreach (subExpr; aggreExpr.getArguments())
368                 {
369                     // version(HUNT_ENTITY_DEBUG_MORE) {
370                     //     tracef("arg expr : %s, arg string : %s",
371                     //         typeid(cast(Object)subExpr).name, SQLUtils.toSQLString(subExpr));
372                     // }
373 
374                     if (cast(SQLIdentifierExpr) subExpr !is null)
375                     {
376                         newArgs.add(subExpr);
377                     }
378                     else if (cast(SQLPropertyExpr) subExpr !is null)
379                     {
380                         SQLPropertyExpr pExpr = cast(SQLPropertyExpr) subExpr;
381                         auto eqlObj = _eqlObj.get(pExpr.getOwnernName(), null);
382                         auto currentFieldName = (cast(SQLPropertyExpr) subExpr).getName();
383                         if (eqlObj !is null)
384                         {
385                             bool isHandled = false;
386                             auto fields = _tableFields.get(eqlObj.className(), null);
387                             if (fields !is null)
388                             {
389                                 foreach (string classMember, EntityFieldInfo fieldInfo; fields)
390                                 {
391                                     string columnName = fieldInfo.getColumnName();
392 
393                                     version(HUNT_ENTITY_DEBUG_MORE) {
394                                         tracef("classMember: %s, columnName: %s, currentField: %s", 
395                                             classMember, columnName, currentFieldName);
396                                     }
397 
398                                     isHandled = classMember == currentFieldName || columnName == currentFieldName;
399                                     if (isHandled)
400                                     {
401                                         newArgs.add(new SQLPropertyExpr(eqlObj.tableName(),
402                                                 fieldInfo.getColumnName()));
403                                         break;
404                                     }
405                                 }
406                             }
407                         
408                             if(!isHandled) {
409                                 string msg = format("Found a undefined memeber [%s] in class [%s]", 
410                                     currentFieldName, eqlObj.className());
411                                 warningf(msg);
412 
413                                 // TODO: Tasks pending completion -@zhangxueping at 2020-10-20T14:32:17+08:00
414                                 // 
415                                 // throw new EntityException(msg);
416                             }
417                         }
418                     }
419                     else
420                     {
421                         newArgs.add(subExpr);
422                     }
423                 }
424                 aggreExpr.getArguments().clear();
425                 aggreExpr.getArguments().addAll(newArgs);
426                 select_copy.addSelectItem(aggreExpr,selectItem.getAlias());
427             }
428             else
429             {
430                 version(HUNT_ENTITY_DEBUG_MORE) {
431                     auto o = cast(Object)expr;
432                     warningf("Expr: %s, item: %s", typeid(o).name, SQLUtils.toSQLString(selectItem));
433                 }
434                 select_copy.addSelectItem(selectItem);
435             }
436         }
437 
438         ///from
439         auto fromExpr = select_copy.getFrom();
440         string[string] aliasModelMap;
441 
442         SQLTableSource  tableSource = cast(SQLTableSource)queryBlock.getFrom();
443         SQLJoinTableSource joinTableSource = cast(SQLJoinTableSource)tableSource;
444         if(joinTableSource !is null) {
445             SQLExprTableSource exprTableSource = cast(SQLExprTableSource)joinTableSource.getLeft();
446             if(exprTableSource !is null) {
447                 SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr)exprTableSource.getExpr(); 
448                 string tableAlias = exprTableSource.getAlias();
449                 
450                 if(!tableAlias.empty()) {
451                     aliasModelMap[tableAlias] = identifierExpr.getName();
452                 }
453             }
454 
455             exprTableSource = cast(SQLExprTableSource)joinTableSource.getRight();
456             if(exprTableSource !is null) {
457                 string tableAlias = exprTableSource.getAlias();
458                 string realName;
459                 
460                 SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr)exprTableSource.getExpr(); 
461                 if(identifierExpr !is null) {
462                     realName = identifierExpr.getName();
463                 } 
464 
465                 SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr)exprTableSource.getExpr(); 
466                 if(propertyExpr !is null) {
467                     realName = propertyExpr.getName();
468                 } 
469                 
470                 if(realName.empty()) {
471                     warningf("The actual type: %s", typeid(cast(Object)identifierExpr));
472                 } else if(!tableAlias.empty()) {
473                     aliasModelMap[tableAlias] = realName;
474                 }
475             }
476         } else {
477             SQLExprTableSource exprTableSource = cast(SQLExprTableSource)tableSource;
478             if(exprTableSource !is null) {
479                 SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr)exprTableSource.getExpr(); 
480                 string tableAlias = tableSource.getAlias();
481                 
482                 if(!tableAlias.empty()) {
483                     aliasModelMap[tableAlias] = identifierExpr.getName();
484                 }
485             } else {
486                 warningf("Can't handle table source: %s", typeid(cast(Object)tableSource));
487             }
488         }
489 
490         version(HUNT_ENTITY_DEBUG) {
491             warning(aliasModelMap);
492         }
493 
494         parseFromTable(fromExpr);
495                
496 
497         ///where 
498         auto whereCond = select_copy.getWhere();
499         if (whereCond !is null) {
500             EqlSubstitutionContext context = new EqlSubstitutionContext();
501             context.modelTableMap = _clsNameToTbName;
502             context.aliasModelMap = aliasModelMap;
503             context.tableFields = _tableFields;
504             // context.eqlObjs = _eqlObj;
505 
506             // substituteInExpress(whereCond, context);
507 
508             // FIXME: Needing refactor or cleanup -@zhangxueping at 2020-09-21T14:59:49+08:00
509             // Remove this block below.
510             
511             string where = SQLUtils.toSQLString(whereCond);
512             version (HUNT_ENTITY_DEBUG) warning(where);
513             where = convertAttrExpr(where);
514             version (HUNT_ENTITY_DEBUG) trace(where);
515             SQLExpr newExpr = SQLUtils.toSQLExpr(where);
516             substituteInExpress(newExpr, context);
517             select_copy.setWhere(newExpr);
518         }
519 
520         ///order by
521         auto orderBy = select_copy.getOrderBy();
522         if (orderBy !is null)
523         {
524             version (HUNT_ENTITY_DEBUG) infof("Parsing the orderBy clause.");
525             foreach (item; orderBy.getItems)
526             {
527                 auto exprStr = SQLUtils.toSQLString(item.getExpr(), _dbtype);
528                 // version (HUNT_ENTITY_DEBUG_MORE)
529                 //     logDebug("order item : %s".format(exprStr));
530                 item.replace(item.getExpr(), SQLUtils.toSQLExpr(convertAttrExpr(exprStr), _dbtype));
531             }
532         }
533         // else
534         // {
535         //     version (HUNT_ENTITY_DEBUG_MORE)
536         //         logDebug("order by item is null");
537         // }
538 
539         /// group by 
540         auto groupBy = select_copy.getGroupBy();
541         if (groupBy !is null)
542         {
543             version (HUNT_ENTITY_DEBUG) infof("Parsing the groupBy clause.");
544             groupBy.getItems().clear();
545             foreach (item; queryBlock.getGroupBy().getItems())
546             {
547                 // logDebug("group item : %s".format(SQLUtils.toSQLString(item)));
548                 groupBy.addItem(SQLUtils.toSQLExpr(convertAttrExpr(SQLUtils.toSQLString(item))));
549             }
550             auto having = groupBy.getHaving();
551             if (having !is null)
552             {
553                 groupBy.setHaving(SQLUtils.toSQLExpr(
554                         convertAttrExpr(SQLUtils.toSQLString(having))));
555             }
556             select_copy.setGroupBy(groupBy);
557         }
558 
559         version(HUNT_ENTITY_DEBUG) info("The parsing done. Now, converting it to native SQL...");
560         _parsedEql = SQLUtils.toSQLString(select_copy, _dbtype);
561         version(HUNT_ENTITY_DEBUG) warning(_parsedEql);
562 
563     }
564 
565     private void doUpdateParse()
566     {
567         SQLUpdateStatement updateBlock = cast(SQLUpdateStatement)(_stmtList.get(0));
568         /// update item
569         foreach (SQLUpdateSetItem updateItem; updateBlock.getItems())
570         {
571             version(HUNT_ENTITY_DEBUG)
572             {
573                 tracef("Update item (name: %s, value: %s)", SQLUtils.toSQLString(updateItem.getColumn()),
574                         SQLUtils.toSQLString(updateItem.getValue));
575             }
576             SQLExpr expr = updateItem.getColumn();
577 
578             if (cast(SQLIdentifierExpr) expr !is null) {
579                 warning("Do nothing");
580             }
581 
582             if (cast(SQLPropertyExpr) expr !is null)
583             {
584                 EqlObject eqlObj = _eqlObj.get((cast(SQLPropertyExpr) expr).getOwnernName(), null);
585                 string currentFieldName = (cast(SQLPropertyExpr) expr).getName();
586                 if (eqlObj !is null)
587                 {
588                     bool isHandled = false;
589                     EntityField fields = _tableFields.get(eqlObj.className(), null);
590                     if (fields !is null)
591                     {
592                         foreach (string classMember, EntityFieldInfo fieldInfo; fields) {
593                             if(fieldInfo.isAggregateType()) {
594                                 // ignore all the aggregative members;
595                                 version(HUNT_DEBUG) {
596                                     infof("Aggregative member ignored: %s", classMember);
597                                 }
598 
599                                 continue;
600                             }
601                             string columnName = fieldInfo.getColumnName();
602 
603                             version(HUNT_ENTITY_DEBUG_MORE) {
604                                 tracef("classMember: %s, columnName: %s, currentField: %s", 
605                                     classMember, columnName, currentFieldName);
606                             }
607 
608                             isHandled = classMember == currentFieldName || columnName == currentFieldName;
609                             if (isHandled) {
610                                 if (_dbtype == DBType.POSTGRESQL.name) {
611                                     // PostgreSQL
612                                     // https://www.postgresql.org/docs/9.1/sql-update.html
613                                     updateItem.setColumn(new SQLIdentifierExpr(columnName));
614                                     // updateItem.setColumn(new SQLPropertyExpr(eqlObj.tableName(),
615                                     //         fieldInfo.getColumnName()));
616                                 } else {
617                                     updateItem.setColumn(new SQLPropertyExpr(eqlObj.tableName(), columnName));
618                                 }
619                                 
620                                 break;
621                             }
622                         }
623                     }
624 
625                     if(!isHandled) {
626                         string msg = format("Found a undefined memeber [%s] in class [%s]", 
627                             currentFieldName, eqlObj.className());
628                         warningf(msg);
629 
630                         // TODO: Tasks pending completion -@zhangxueping at 2020-10-20T14:32:17+08:00
631                         // 
632                         // throw new EntityException(msg);
633                     }
634                 }
635                 else
636                 {
637                     eql_throw("Statement",
638                         " undefined sql object  '%s' in '%s': ".format((cast(SQLPropertyExpr) expr).getOwnernName(),
639                         SQLUtils.toSQLString(expr)));
640                 }
641             }
642 
643             auto valueExpr = updateItem.getValue();
644             if (cast(SQLPropertyExpr) valueExpr !is null)
645             {
646                 auto eqlObj = _eqlObj.get((cast(SQLPropertyExpr) valueExpr).getOwnernName(), null);
647                 string currentFieldName = (cast(SQLPropertyExpr) valueExpr).getName();
648                 if (eqlObj !is null)
649                 {
650                     bool isHandled = false;
651                     EntityField fields = _tableFields.get(eqlObj.className(), null);
652                     if (fields !is null)
653                     {
654                         foreach (string classMember, EntityFieldInfo fieldInfo; fields)
655                         {
656                             string columnName = fieldInfo.getColumnName();
657 
658                             version(HUNT_ENTITY_DEBUG_MORE) {
659                                 tracef("classMember: %s, columnName: %s, currentField: %s", 
660                                     classMember, columnName, currentFieldName);
661                             }
662 
663                             isHandled = classMember == currentFieldName || columnName == currentFieldName;
664                             if (isHandled)
665                             {
666                                 updateItem.setValue(new SQLPropertyExpr(eqlObj.tableName(),
667                                         columnName));
668                                 break;
669                             }
670                             // tracef("sql replace : (%s ,%s) ", classMember, fieldInfo.getColumnName());
671                         }
672                     }
673                     
674                     if(!isHandled) {
675                         string msg = format("Found a undefined memeber [%s] in class [%s]", 
676                             currentFieldName, eqlObj.className());
677                         warningf(msg);
678 
679                         // TODO: Tasks pending completion -@zhangxueping at 2020-10-20T14:32:17+08:00
680                         // 
681                         // throw new EntityException(msg);
682                     }
683                 }
684             }
685         }
686 
687         ///from
688         auto fromExpr = updateBlock.getTableSource();
689         // logDebug("update From : %s".format(SQLUtils.toSQLString(fromExpr)));
690         parseFromTable(fromExpr);
691 
692         ///where 
693         SQLExpr whereCond = updateBlock.getWhere();
694         if (whereCond !is null)
695         {
696             string where = SQLUtils.toSQLString(whereCond);
697             where = convertAttrExpr(where);
698             updateBlock.setWhere(SQLUtils.toSQLExpr(where));
699         }
700 
701         version(HUNT_ENTITY_DEBUG) info("The parsing done. Now, converting it to native SQL...");
702         _parsedEql = SQLUtils.toSQLString(updateBlock, _dbtype);
703         version (HUNT_ENTITY_DEBUG) {
704             warning(_parsedEql);
705         }
706     }
707 
708     private void doDeleteParse()
709     {
710         auto delBlock = cast(SQLDeleteStatement)(_stmtList.get(0));
711 
712         ///from
713         auto fromExpr = delBlock.getTableSource();
714         // logDebug("delete From : %s".format(SQLUtils.toSQLString(fromExpr)));
715 
716         parseFromTable(fromExpr);
717 
718         ///where 
719         auto whereCond = delBlock.getWhere();
720         if (whereCond !is null)
721         {
722             auto where = SQLUtils.toSQLString(whereCond);
723             where = convertAttrExpr(where);
724             delBlock.setWhere(SQLUtils.toSQLExpr(where));
725         }
726 
727         _parsedEql = SQLUtils.toSQLString(delBlock, _dbtype);
728     }
729 
730     private void doInsertParse()
731     {
732         SQLInsertStatement insertBlock = cast(SQLInsertStatement)(_stmtList.get(0));
733 
734         List!SQLExpr newColumns = new ArrayList!SQLExpr();
735 
736         /// insert item
737         foreach (expr; insertBlock.getColumns())
738         {
739             version(HUNT_ENTITY_DEBUG_MORE)
740                 trace("insert item :", SQLUtils.toSQLString(expr));
741             if (cast(SQLIdentifierExpr) expr !is null)
742             {
743                 newColumns.add(expr);
744             }
745             if (cast(SQLPropertyExpr) expr !is null)
746             {
747                 SQLPropertyExpr pExpr = cast(SQLPropertyExpr) expr;
748                 auto eqlObj = _eqlObj.get(pExpr.getOwnernName(), null);
749                 string currentFieldName = (cast(SQLPropertyExpr) expr).getName();
750                 if (eqlObj !is null)
751                 {
752                     bool isHandled = false;
753                     auto fields = _tableFields.get(eqlObj.className(), null);
754                     if (fields !is null)
755                     {
756                         foreach (classMember, fieldInfo; fields)
757                         {
758                             string columnName = fieldInfo.getColumnName();
759 
760                             version(HUNT_ENTITY_DEBUG_MORE) {
761                                 tracef("classMember: %s, columnName: %s, currentField: %s", 
762                                     classMember, columnName, currentFieldName);
763                             }
764 
765                             isHandled = classMember == currentFieldName || columnName == currentFieldName;
766 
767                             if (isHandled)
768                             {
769                                 newColumns.add(new SQLIdentifierExpr(fieldInfo.getColumnName()));
770                                 break;
771                             }
772                         }
773                         
774                         if(!isHandled) {
775                             string msg = format("Found a undefined memeber [%s] in class [%s]", 
776                                 currentFieldName, eqlObj.className());
777                             warningf(msg);
778 
779                             // TODO: Tasks pending completion -@zhangxueping at 2020-10-20T14:32:17+08:00
780                             // 
781                             // throw new EntityException(msg);
782                         }
783                     }
784                 }
785                 else
786                 {
787                     eql_throw("Statement",
788                     " undefined sql object  '%s' in '%s': ".format(pExpr.getOwnernName(),SQLUtils.toSQLString(pExpr)));
789                 }
790             }
791             insertBlock.getColumns().clear();
792             insertBlock.getColumns().addAll(newColumns);
793 
794             // auto valueExpr = updateItem.getValue();
795             // if (cast(SQLPropertyExpr) valueExpr !is null)
796             // {
797             //     auto eqlObj = _eqlObj.get((cast(SQLPropertyExpr) valueExpr).getOwnernName(), null);
798             //     auto clsFieldName = (cast(SQLPropertyExpr) valueExpr).getName();
799             //     if (eqlObj !is null)
800             //     {
801             //         auto fields = _tableFields.get(eqlObj.className(), null);
802             //         if (fields !is null)
803             //         {
804             //             foreach (classMember, fieldInfo; fields)
805             //             {
806             //                 if (classMember == clsFieldName)
807             //                 {
808             //                     updateItem.setValue(new SQLPropertyExpr(eqlObj.tableName(),
809             //                             fieldInfo.getColumnName()));
810             //                     break;
811             //                 }
812             //                 // logDebug("sql replace : (%s ,%s) ".format(k ~ "." ~ classMember,k ~ "." ~ fieldInfo.getColumnName()));
813             //             }
814             //         }
815             //     }
816             // }
817         }
818 
819         ///from
820         SQLExprTableSource fromExpr = insertBlock.getTableSource();
821         // version (HUNT_DEBUG)
822         //     logDebug("Insert into: %s".format(SQLUtils.toSQLString(fromExpr)));
823         parseFromTable(fromExpr);
824 
825         _parsedEql = SQLUtils.toSQLString(insertBlock, _dbtype);
826 
827         if(_dbtype == DBType.POSTGRESQL) {
828             string autoIncrementKey = _entityInfo.autoIncrementKey;
829             _parsedEql = _parsedEql.stripRight(";");
830             if(!autoIncrementKey.empty()) {
831                 _parsedEql ~= " RETURNING " ~ autoIncrementKey ~ ";";
832             }
833         }
834     }
835 
836     /// a.id  --- > Class.id , a is instance of Class
837     private string convertExprAlias(SQLPropertyExpr expr)
838     {
839         string originStr = SQLUtils.toSQLString(expr);
840         auto objName = expr.getOwnernName();
841         auto subPropertyName = expr.getName();
842         auto aliasMap = _aliasVistor.getAliasMap();
843         string clsName;
844         foreach (obj, v; aliasMap)
845         {
846             if (obj == objName)
847             {
848                 auto exprTab = (cast(SQLExprTableSource) v).getExpr();
849                 if (cast(SQLIdentifierExpr) exprTab !is null)
850                 {
851                     expr.setOwner(exprTab);
852                 }
853                 else if (cast(SQLPropertyExpr) exprTab !is null)
854                 {
855                     expr.setOwner(SQLUtils.toSQLExpr(
856                             convertExprAlias(cast(SQLPropertyExpr) exprTab)));
857                 }
858             }
859         }
860         // logDebug("get last type : ( %s , %s )".format(originStr,SQLUtils.toSQLString(expr)));
861         return SQLUtils.toSQLString(expr);
862     }
863 
864     /// a.id --> Table.col
865     private string convertAttrExpr(string attrExpr)
866     {
867         string res = attrExpr;
868         auto conds = matchAll(attrExpr, regex("([^\\(\\s]+)\\.([^\\s]+)"));
869         bool[string] handerFlag;
870         foreach (cond; conds)
871         {
872             string newCond = cond.captures[0];
873             if (newCond in handerFlag)
874                 continue;
875             else
876                 handerFlag[newCond] = true;
877             
878             string owner = cond.captures[1];
879             auto eqlObj = _eqlObj.get(owner, null);
880             if (eqlObj !is null)
881             {
882                 newCond = newCond.replace(owner ~ ".", eqlObj.tableName() ~ ".");
883                 auto fields = _tableFields.get(eqlObj.className(), null);
884                 if (fields !is null)
885                 {
886                     foreach (classMember, fieldInfo; fields)
887                     {
888                         if (classMember == cond.captures[2])
889                             newCond = newCond.replace("." ~ cond.captures[2],
890                                     "." ~ fieldInfo.getColumnName());
891 
892                     }
893                 }
894             }
895             res = res.replace(cond.captures[0], newCond);
896         }
897         return res;
898     }
899 
900     /// remove alias & a.xx -- > Table
901     private void parseFromTable(SQLTableSource fromExpr)
902     {
903         version (HUNT_ENTITY_DEBUG) infof("Parsing the FROM clause. The type is: %s", typeid(cast(Object)fromExpr));
904         if (fromExpr is null)
905         {
906             eql_throw("Table", "no found table");
907         }
908         // logDebug(" From table : %s".format(SQLUtils.toSQLString(fromExpr)));
909         if (cast(SQLJoinTableSource) fromExpr !is null)
910         {
911             auto joinExpr = cast(SQLJoinTableSource) fromExpr;
912             auto rightExpr = cast(SQLExprTableSource)(joinExpr.getRight());
913 
914             auto defaultJoinCond = joinExpr.getCondition();
915             if (defaultJoinCond is null)
916             {
917                 // logDebug("join table no default condition");
918             }
919             else
920             {
921                 auto convertAttrStr = convertAttrExpr(SQLUtils.toSQLString(defaultJoinCond));
922                 // logDebug(" join Cond : %s , convert : %s ".format(SQLUtils.toSQLString(defaultJoinCond),convertAttrStr));
923                 joinExpr.setCondition(SQLUtils.toSQLExpr(convertAttrStr));
924             }
925 
926             if (cast(SQLJoinTableSource)(joinExpr.getLeft()) !is null)
927             {
928                 auto subExpr = cast(SQLJoinTableSource)(joinExpr.getLeft());
929                 parseFromTable(subExpr);
930             }
931             else if (cast(SQLExprTableSource)(joinExpr.getLeft()) !is null)
932             {
933                 auto leftExpr = cast(SQLExprTableSource)(joinExpr.getLeft());
934 
935                 if (cast(SQLPropertyExpr)(leftExpr.getExpr()) !is null)
936                 {
937                     string convertStr = convertExprAlias(cast(SQLPropertyExpr)(leftExpr.getExpr()));
938                     string clsName = _objType.get(convertStr, null);
939                     if (clsName !is null)
940                     {
941                         auto tableName = _clsNameToTbName.get(clsName, null);
942                         if (tableName !is null)
943                         {
944                             leftExpr.setExpr(tableName);
945                         }
946                     }
947 
948                     Object joinCond = _joinConds.get(convertStr, null);
949                     version(HUNT_ENTITY_DEBUG) {
950                         logDebugf("add cond (left): ( %s , %s ), convertStr: %s", clsName, joinCond, convertStr);
951                     }
952                     
953                     if (joinCond !is null)
954                     {
955                         joinExpr.setCondition(SQLUtils.toSQLExpr(joinCond.toString()));
956                     }
957                 }
958                 else if (cast(SQLIdentifierExpr)(leftExpr.getExpr()) !is null)
959                 {
960                     auto clsName = (cast(SQLIdentifierExpr)(leftExpr.getExpr())).getName();
961                     auto tableName = _clsNameToTbName.get(clsName, null);
962                     if (tableName !is null)
963                     {
964                         leftExpr.setExpr(tableName);
965                     }
966                 }
967                 leftExpr.setAlias("");
968             }
969 
970             if (rightExpr is null)
971                 return;
972             if (cast(SQLPropertyExpr)(rightExpr.getExpr()) !is null)
973             {
974                 auto convertStr = convertExprAlias(cast(SQLPropertyExpr)(rightExpr.getExpr()));
975                 auto clsName = _objType.get(convertStr, null);
976                 if (clsName !is null)
977                 {
978                     auto tableName = _clsNameToTbName.get(clsName, null);
979                     if (tableName !is null)
980                     {
981                         rightExpr.setExpr(tableName);
982                     }
983                 }
984                 auto joinCond = _joinConds.get(convertStr, null);
985                 warning(_joinConds);
986                 version(HUNT_ENTITY_DEBUG) {
987                     logDebugf("add cond (right) : ( %s , %s ), convertStr: %s", clsName, joinCond, convertStr);
988                 }
989 
990                 if (joinCond !is null)
991                 {
992                     joinExpr.setCondition(SQLUtils.toSQLExpr(joinCond.toString()));
993                 }
994             }
995             else if (cast(SQLIdentifierExpr)(rightExpr.getExpr()) !is null)
996             {
997                 auto clsName = (cast(SQLIdentifierExpr)(rightExpr.getExpr())).getName();
998                 auto tableName = _clsNameToTbName.get(clsName, null);
999                 if (tableName !is null)
1000                 {
1001                     rightExpr.setExpr(tableName);
1002                 }
1003             }
1004 
1005             rightExpr.setAlias("");
1006         }
1007         else
1008         {
1009             auto expr = cast(SQLExprTableSource)(fromExpr);
1010             if (expr is null)
1011                 return;
1012             if (cast(SQLPropertyExpr)(expr.getExpr()) !is null)
1013             {
1014                 auto convertStr = convertExprAlias(cast(SQLPropertyExpr)(expr.getExpr()));
1015                 auto clsName = _objType.get(convertStr, null);
1016                 if (clsName !is null)
1017                 {
1018                     auto tableName = _clsNameToTbName.get(clsName, null);
1019                     if (tableName !is null)
1020                     {
1021                         expr.setExpr(tableName);
1022                     }
1023                 }
1024 
1025             }
1026             else if (cast(SQLIdentifierExpr)(expr.getExpr()) !is null)
1027             {
1028                 auto clsName = (cast(SQLIdentifierExpr)(expr.getExpr())).getName();
1029                 auto tableName = _clsNameToTbName.get(clsName, null);
1030                 if (tableName !is null)
1031                 {
1032                     expr.setExpr(tableName);
1033                 }
1034             }
1035             expr.setAlias("");
1036         }
1037     }
1038 
1039     public void setParameter(R)(int idx, R param)
1040     {
1041         static if (is(R == int) || is(R == uint))
1042         {
1043             _params[idx] = new Integer(param);
1044         }
1045         else static if (is(R == char))
1046         {
1047             _params[idx] = new String(cast(string)[param]);
1048         }
1049         else static if (is(R == string))
1050         {
1051             _params[idx] = new String(param);
1052         }
1053         else static if (is(R == byte[]) || is(R == ubyte[]))
1054         {
1055             _params[idx] = new Bytes(cast(byte[]) param);
1056         }
1057         else static if (is(R == bool))
1058         {
1059             _params[idx] = new Boolean(param);
1060         }
1061         else static if (is(R == double))
1062         {
1063             _params[idx] = new Double(param);
1064         }
1065         else static if (is(R == float))
1066         {
1067             _params[idx] = new Float(param);
1068         }
1069         else static if (is(R == short) || is(R == ushort))
1070         {
1071             _params[idx] = new Short(param);
1072         }
1073         else static if (is(R == long) || is(R == ulong))
1074         {
1075             _params[idx] = new Long(param);
1076         }
1077         else static if (is(R == byte) || is(R == ubyte))
1078         {
1079             _params[idx] = new Byte(param);
1080         }
1081         else static if (is(R == class))
1082         {
1083             _params[idx] = param;
1084         }
1085         else
1086         {
1087             eql_throw("setParameter", "IllegalArgument not support : " ~ R.stringof);
1088         }
1089     }
1090 
1091     public void setParameter(R)(string key, R param)
1092     {
1093         static if (is(R == int) || is(R == uint))
1094         {
1095             _parameters[key] = new Integer(param);
1096         }
1097         else static if (is(R == char))
1098         {
1099             _parameters[key] = new String(cast(string)[param]);
1100         }
1101         else static if (is(R == string))
1102         {
1103             _parameters[key] = new String(param);
1104         }
1105         else static if (is(R == byte[]) || is(R == ubyte[]))
1106         {
1107             _parameters[key] = new Bytes(cast(byte[]) param);
1108         }
1109         // else static if (is(R == string) || is(R == char) || is(R == byte[]))
1110         // {
1111         //     _parameters[key] = new String(param);
1112         // }
1113         else static if (is(R == bool))
1114         {
1115             _parameters[key] = new Boolean(param);
1116         }
1117         else static if (is(R == double))
1118         {
1119             _parameters[key] = new Double(param);
1120         }
1121         else static if (is(R == float))
1122         {
1123             _parameters[key] = new Float(param);
1124         }
1125         else static if (is(R == short) || is(R == ushort))
1126         {
1127             _parameters[key] = new Short(param);
1128         }
1129         else static if (is(R == long) || is(R == ulong))
1130         {
1131             _parameters[key] = new Long(param);
1132         }
1133         else static if (is(R == byte) || is(R == ubyte))
1134         {
1135             _parameters[key] = new Byte(param);
1136         }
1137         else static if (is(R == class))
1138         {
1139             _parameters[key] = param;
1140         }
1141         else
1142         {
1143             eql_throw("setParameter", "IllegalArgument not support : " ~ R.stringof);
1144         }
1145     }
1146 
1147     public void putClsTbName(string clsName, string tableName)
1148     {
1149         _clsNameToTbName[clsName] = tableName;
1150     }
1151 
1152     public void putFields(string table, EntityField ef)
1153     {
1154         version(HUNT_ENTITY_DEBUG) {
1155             infof("table: %s", table);
1156             foreach (string classMember, EntityFieldInfo fieldInfo; ef) {
1157                     tracef("classMember: %s,  isAggregateType: %s, fullColumn: [%s]", 
1158                         classMember, fieldInfo.isAggregateType(), fieldInfo.getFullColumn());
1159                 }  
1160         }      
1161         _tableFields[table] = ef;
1162     }
1163 
1164     public void putJoinCond(Object[string] conds)
1165     {
1166         foreach (k, v; conds)
1167         {
1168             _joinConds[k] = v;
1169         }
1170     }
1171 
1172     public string getTableName(string clsName)
1173     {
1174         return _clsNameToTbName.get(clsName, null);
1175     }
1176 
1177     public string getNativeSql()
1178     {
1179         if(!_nativeSql.empty())
1180             return _nativeSql;
1181 
1182         string sql = _parsedEql;
1183 
1184         version (HUNT_ENTITY_DEBUG_MORE)
1185             logDebug("EQL params : ", _parameters);
1186 
1187         foreach (k, v; _parameters)
1188         {
1189             auto re = regex(r":" ~ k ~ r"([^\w]*)", "g");
1190             if (cast(String) v !is null || (cast(Nullable!string) v !is null))
1191             {
1192                 version (HUNT_ENTITY_DEBUG_MORE)
1193                     logInfo("-----: ", v.toString);
1194                 if (_dbtype == DBType.POSTGRESQL.name)
1195                     sql = sql.replaceAll(re, quoteSqlString(v.toString(), "'") ~ "$1");
1196                 else
1197                     sql = sql.replaceAll(re, quoteSqlString(v.toString()) ~ "$1");
1198             }
1199             else
1200             {
1201                 sql = sql.replaceAll(re, v.toString() ~ "$1");
1202             }
1203         }
1204 
1205         if (_params.length > 0)
1206         {
1207             auto keys = _params.keys;
1208             sort!("a < b")(keys);
1209             List!Object params = new ArrayList!Object();
1210             foreach (e; keys)
1211             {
1212                 params.add(_params[e]);
1213             }
1214             sql = SQLUtils.format(sql, _dbtype, params, _formatOption);
1215             // sql = SQLUtils.format(sql, _dbtype, params);
1216         } else {
1217             sql = SQLUtils.format(sql, _dbtype, _formatOption);
1218             // sql = SQLUtils.format(sql, _dbtype);
1219         }
1220 
1221         // FIXME: Needing refactor or cleanup -@zhangxueping at 2019-10-09T14:41:55+08:00
1222         // why?
1223         // if (_dbtype == DBType.POSTGRESQL.name && _params.length == 0) {
1224         //     sql = SQLUtils.format(sql, _dbtype, _formatOption);
1225         //     warning(sql);
1226         // }
1227 
1228         version(HUNT_ENTITY_DEBUG) infof("result sql : %s", sql);
1229         _nativeSql = sql;
1230         return sql;
1231     }
1232 
1233 
1234     // SQLInSubQueryExpr
1235     static void substitute(SQLInSubQueryExpr subQueryExpr, EqlSubstitutionContext context) {
1236         version (HUNT_ENTITY_DEBUG) tracef("Handling a SQLInSubQueryExpr");
1237 
1238         // in express
1239         SQLPropertyExpr expr =  cast(SQLPropertyExpr)subQueryExpr.getExpr();
1240         string ownerName = expr.getOwnernName();
1241         auto itemPtr = ownerName in context.aliasModelMap;
1242         if(itemPtr !is null) {
1243             ownerName = *itemPtr;
1244             ownerName = context.modelTableMap.get(ownerName, ownerName);
1245             expr.setOwner(ownerName);
1246         }
1247 
1248         // version (HUNT_ENTITY_DEBUG) warningf("ownerName: %s", ownerName);
1249 
1250         // select clause
1251         substitute(subQueryExpr.getSubQuery(), context);
1252 
1253     }
1254 
1255     // SQLIdentifierExpr
1256     static void substitute(SQLIdentifierExpr expr, EqlSubstitutionContext context) {
1257         version (HUNT_ENTITY_DEBUG) tracef("Handling an SQLIdentifierExpr");
1258         string className = expr.getName();
1259         // version (HUNT_ENTITY_DEBUG) tracef("className: %s",  className);
1260 
1261         auto itemPtr = className in context.modelTableMap;
1262         if(itemPtr is null) {
1263             version (HUNT_ENTITY_DEBUG) warningf("No mapped model name found for class: %s", className);
1264         } else {
1265             expr.setName(*itemPtr);
1266         }
1267     }
1268 
1269     // SQLPropertyExpr
1270     static void substitute(SQLPropertyExpr expr, EqlSubstitutionContext context) {
1271         version (HUNT_ENTITY_DEBUG) tracef("Handling an SQLPropertyExpr");
1272 
1273         string ownerName = expr.getOwnernName();
1274         string fieldName = expr.getName();
1275 
1276         if(ownerName.empty()) {
1277             warningf("No owner found before [%s]", fieldName);
1278             return;
1279         }
1280 
1281         version (HUNT_ENTITY_DEBUG) trace(context.tableFields.keys);
1282         
1283         // owner
1284         string modelName = context.getModelByAlias(ownerName, ownerName);
1285         string tableName = context.getTableByModel(modelName, modelName);
1286 
1287         if(tableName != ownerName) {
1288             expr.setOwner(tableName);
1289         }
1290 
1291         version (HUNT_ENTITY_DEBUG) {
1292             tracef("ownerName, alias: %s, model: %s, table: %s", ownerName, modelName, tableName);
1293         }
1294 
1295         // name
1296         EntityFieldInfo[string] fields = context.tableFields.get(modelName, null);
1297         foreach (string member, EntityFieldInfo fieldInfo; fields) {
1298             if(member == fieldName) {
1299                 string columnName = fieldInfo.getColumnName();
1300                 version (HUNT_ENTITY_DEBUG_MORE) {
1301                     tracef("The field's name is substituted from [%s] to [%s]", fieldName, columnName);
1302                 }
1303                 expr.setName(columnName);
1304                 break;
1305             }
1306         }
1307 
1308         // TODO: Tasks pending completion -@zhangxueping at 2020-09-21T11:41:18+08:00
1309         // a => *
1310     }
1311 
1312 
1313     // SQLBinaryOpExpr
1314     static void substitute(SQLBinaryOpExpr expr, EqlSubstitutionContext context) {
1315         version (HUNT_ENTITY_DEBUG) tracef("Handling an SQLBinaryOpExpr");
1316 
1317         // Left
1318         SQLExpr leftExpr = expr.getLeft();
1319         substituteInExpress(leftExpr, context);
1320 
1321         // Right
1322         SQLExpr rightExpr = expr.getRight();
1323         substituteInExpress(rightExpr, context);
1324     }
1325     
1326     static void substitute(SQLNumericLiteralExpr expr, EqlSubstitutionContext context) {
1327         version (HUNT_ENTITY_DEBUG) tracef("Handling an SQLNumericLiteralExpr");
1328         // do nothing
1329     }
1330     
1331     static void substitute(SQLTextLiteralExpr expr, EqlSubstitutionContext context) {
1332         version (HUNT_ENTITY_DEBUG) tracef("Handling an SQLTextLiteralExpr");
1333         // do nothing
1334     }
1335     
1336     static void substitute(SQLInListExpr expr, EqlSubstitutionContext context) {
1337         version (HUNT_ENTITY_DEBUG) tracef("Handling an SQLInListExpr");
1338         SQLExpr sqlExpr = expr.getExpr();
1339 
1340         substituteInExpress(sqlExpr, context);
1341 
1342         //
1343         List!SQLExpr targets = expr.getTargetList();
1344         foreach(SQLExpr se; targets) {
1345             substituteInExpress(se, context);
1346         }
1347     }
1348 
1349     static void substituteInExpress(SQLExpr sqlExpr, EqlSubstitutionContext context) {
1350         version (HUNT_ENTITY_DEBUG) infof("Handling an express: %s", typeid(cast(Object)sqlExpr));
1351 
1352         SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr)sqlExpr;
1353         if(identifierExpr !is null) {
1354             substitute(identifierExpr, context);
1355             return;
1356         }
1357 
1358         SQLPropertyExpr propertyExpr = cast(SQLPropertyExpr)sqlExpr;
1359         if(propertyExpr !is null) {
1360             substitute(propertyExpr, context);
1361             return;
1362         }
1363 
1364         SQLInSubQueryExpr subQueryExpr = cast(SQLInSubQueryExpr)sqlExpr;
1365         if(subQueryExpr !is null) {
1366             substitute(subQueryExpr, context);
1367             return;
1368         }
1369 
1370         SQLBinaryOpExpr opExpr = cast(SQLBinaryOpExpr)sqlExpr;
1371         if(opExpr !is null) {
1372             substitute(opExpr, context);
1373             return;
1374         }
1375 
1376         SQLNumericLiteralExpr numberExpr = cast(SQLNumericLiteralExpr)sqlExpr;
1377         if(numberExpr !is null) {
1378             substitute(numberExpr, context);
1379             return;
1380         }
1381 
1382         SQLTextLiteralExpr textExpr = cast(SQLTextLiteralExpr)sqlExpr;
1383         if(textExpr !is null) {
1384             substitute(textExpr, context);
1385             return;
1386         }
1387         
1388         SQLInListExpr inListExpr = cast(SQLInListExpr)sqlExpr;
1389         if(inListExpr !is null) {
1390             substitute(inListExpr, context);
1391             return;
1392         }
1393 
1394         warningf("A express can't be handled: %s", typeid(cast(Object)sqlExpr));
1395     }
1396 
1397     static void substitute(SQLSelect subSelect, EqlSubstitutionContext context) {
1398         SQLSelectQueryBlock queryBlock = cast(SQLSelectQueryBlock)subSelect.getQuery();
1399 
1400         // The FROM clause 
1401         SQLExprTableSource  fromExpr = cast(SQLExprTableSource)queryBlock.getFrom();
1402         string tableAlias = fromExpr.getAlias();
1403         if(!tableAlias.empty()) {
1404             fromExpr.setAlias("");
1405         
1406             SQLIdentifierExpr identifierExpr = cast(SQLIdentifierExpr)fromExpr.getExpr();
1407             if(identifierExpr !is null) {
1408                 version (HUNT_ENTITY_DEBUG) tracef("Removing the alias: %s", tableAlias);
1409                 context.aliasModelMap[tableAlias] = identifierExpr.getName();
1410             }
1411         }
1412 
1413         SQLExpr sqlExpr = fromExpr.getExpr();
1414         substituteInExpress(sqlExpr, context);
1415 
1416         // The selected fields
1417         foreach (SQLSelectItem selectItem; queryBlock.getSelectList()) {
1418             SQLExpr expr = selectItem.getExpr();
1419             substituteInExpress(expr, context);
1420         }
1421 
1422         // where
1423         SQLExpr whereExpr = queryBlock.getWhere();
1424         if(whereExpr !is null) {
1425             substituteInExpress(whereExpr, context);
1426         }
1427     }
1428 }