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.criteria.CriteriaBuilder;
13 
14 import hunt.entity;
15 import hunt.entity.dialect;
16 import hunt.Exceptions;
17 import hunt.logging;
18 
19 import std.conv;
20 import std.format;
21 import std.traits;
22 import std.variant;
23 
24 class CriteriaBuilder
25 {
26 
27     private EntityManagerFactory _factory;
28     private EntityManager _manager;
29 
30     this(EntityManagerFactory factory) {
31         _factory = factory;
32     }
33 
34     CriteriaBuilder setManager(EntityManager manager) {
35         _manager = manager;
36         return this;
37     }
38 
39     EntityManager getManager() {return _manager;}
40 
41     QueryBuilder createQueryBuilder() {
42         return _factory.createQueryBuilder();
43     }
44 
45     Dialect getDialect() {
46         return _factory.getDialect();
47     } 
48 
49     // Database getDatabase() {
50     //     return _factory.getDatabase();
51     // }
52 
53     CriteriaQuery!(T,F) createQuery(T : Object,F : Object = T)() {
54         return new CriteriaQuery!(T,F)(this);
55     }
56 
57     CriteriaDelete!(T,F) createCriteriaDelete(T : Object,F : Object = T)() {
58         return new CriteriaDelete!(T,F)(this);
59     }
60 
61     CriteriaUpdate!(T,F) createCriteriaUpdate(T : Object,F : Object = T)() {
62         return new CriteriaUpdate!(T,F)(this);
63     }
64 
65     Order asc(EntityFieldInfo info) {
66         return new Order(info.getFullColumn(), OrderBy.ASC);
67     }
68 
69     Order desc(EntityFieldInfo info) {
70         return new Order(info.getFullColumn(), OrderBy.DESC);
71     }
72 
73     //P should be Predicate
74     Predicate and(P...)(P predicates) {
75         return new Predicate().andValue(predicates);
76     }
77 
78     //P should be Predicate
79     Predicate or(P...)(P predicates) {
80         return new Predicate().orValue(predicates);
81     }
82 
83     EntityExpression max(EntityFieldInfo info) {
84         return new EntityExpression(info.getColumnName(), info.getTableName()).setColumnSpecifier("MAX");
85     }
86 
87     EntityExpression min(EntityFieldInfo info) {
88         return new EntityExpression(info.getColumnName(), info.getTableName()).setColumnSpecifier("MIN");
89     }
90 
91     EntityExpression avg(EntityFieldInfo info) {
92         return new EntityExpression(info.getColumnName(), info.getTableName()).setColumnSpecifier("AVG");
93     }
94 
95     EntityExpression sum(EntityFieldInfo info) {
96         return new EntityExpression(info.getColumnName(), info.getTableName()).setColumnSpecifier("SUM");
97     }
98 
99     EntityExpression count(EntityFieldInfo info) {
100         return new EntityExpression(info.getColumnName(), info.getTableName()).setColumnSpecifier("COUNT");
101     }
102 
103     // deprecated("Unsupported any more")
104     EntityExpression count(T)(Root!T root) {
105         return new EntityExpression(root.getPrimaryField().getColumnName(), root.getPrimaryField().getTableName()).setColumnSpecifier("COUNT");
106     }
107 
108     EntityExpression countDistinct(EntityFieldInfo info) {
109         return new EntityExpression(info.getColumnName(), info.getTableName()).setDistinct(true).setColumnSpecifier("COUNT");
110     }
111 
112     // deprecated("Unsupported any more")
113     EntityExpression countDistinct(T)(Root!T root) {
114         return new EntityExpression(root.getPrimaryField().getColumnName(), root.getPrimaryField().getTableName()).setDistinct(true).setColumnSpecifier("COUNT");
115     }
116 
117     Predicate equal(T)(EntityFieldInfo info, T t, bool check = true) {
118         static if (isBuiltinType!T) {
119             if (check)
120                 assertType!(T)(info);
121             return new Predicate().addValue(info.getFullColumn(), "=", quoteSqlfNeed(t));
122         }
123         else {
124             Predicate p;
125             if (info.getJoinColumn() != "") {
126                 auto entityInfo = new EntityInfo!(T,T)(_manager, t);
127                 p = new Predicate().addValue(info.getJoinColumn(), "=", quoteSqlfNeed(entityInfo.getPrimaryValue()));
128             }
129             else {
130                 throw new EntityException("cannot compare field %s with type %".format(info.getFieldName(), typeid(T).stringof));
131             }
132             return p;
133         }
134     }
135 
136 
137     /// for get lazy data
138     Predicate lazyEqual(T)(EntityFieldInfo info, T t, bool check = true) {
139         static if (isBuiltinType!T) {
140             if (check)
141                 assertType!(T)(info);
142                 return new Predicate().addValue(info.getFullColumn(), "=", t);
143         }
144         else {
145             Predicate p;
146             if (info.getJoinColumn() != "") {
147                 auto entityInfo = new EntityInfo!(T,T)(_manager, t);
148                 p = new Predicate().addValue(info.getJoinColumn(), "=", quoteSqlfNeed(entityInfo.getPrimaryValue()));
149             }
150             else {
151                 throw new EntityException("cannot compare field %s with type %".format(info.getFieldName(), typeid(T).stringof));
152             }
153             return p;
154         }
155     }
156 
157     /// for get lazy data
158     Predicate lazyManyToManyEqual(T)(EntityFieldInfo info, T t, bool check = true) {
159         auto condTable = info.getJoinTable();
160         auto condColumn = info.getJoinColumn();
161         if(info.isMainMapped())
162             condColumn = info.getInverseJoinColumn();
163         static if (isBuiltinType!T) {
164             if (check)
165                 assertType!(T)(info);
166                 return new Predicate().addValue(condTable~"."~condColumn, "=", t);
167         }
168         else {
169             Predicate p;
170             if (info.getJoinColumn() != "") {
171                 auto entityInfo = new EntityInfo!(T,T)(_manager, t);
172                 p = new Predicate().addValue(condTable~"."~condColumn, "=", quoteSqlfNeed(entityInfo.getPrimaryValue()));
173             }
174             else {
175                 throw new EntityException("cannot compare field %s with type %".format(info.getFieldName(), typeid(T).stringof));
176             }
177             return p;
178         }
179     }
180 
181     Predicate equal(EntityFieldInfo info) {
182         Variant va = info.getColumnFieldData();
183         if(va.type == typeid(string)) {
184             string value = va.get!string();
185             return new Predicate().addValue(info.getFullColumn(), "=", escapeWithQuotes(value));
186         } else {
187             return new Predicate().addValue(info.getFullColumn(), "=", info.getColumnFieldData().toString());
188         }
189     }
190 
191     Predicate notEqual(T)(EntityFieldInfo info, T t){
192 
193         static if (isBuiltinType!T) {
194             if (check)
195                 assertType!(T)(info);
196             return new Predicate().addValue(info.getFullColumn(), "<>", quoteSqlfNeed(t));
197         }
198         else {
199             Predicate p;
200             if (info.getJoinColumn() != "") {
201                 auto entityInfo = new EntityInfo!(T,T)(_manager, t);
202                 p = new Predicate().addValue(info.getJoinColumn(), "<>", quoteSqlfNeed(entityInfo.getPrimaryValue()));
203             }
204             else {
205                 throw new EntityException("cannot compare field %s with type %".format(info.getFieldName(), typeid(T).stringof));
206             }
207             return p;
208         }
209     }
210 
211     Predicate notEqual(EntityFieldInfo info){
212         return new Predicate().addValue(info.getFullColumn(), "<>", info.getColumnFieldData().toString());
213     }
214 
215     Predicate gt(T)(EntityFieldInfo info, T t){
216         assertType!(T)(info);
217         return new Predicate().addValue(info.getFullColumn(), ">", quoteSqlfNeed(t));
218     }
219 
220     Predicate gt(EntityFieldInfo info){
221         return new Predicate().addValue(info.getFullColumn(), ">", info.getColumnFieldData().toString());
222     }
223 
224     Predicate ge(T)(EntityFieldInfo info, T t){
225         assertType!(T)(info);
226         return new Predicate().addValue(info.getFullColumn(), ">=", quoteSqlfNeed(t));
227     }
228 
229     Predicate ge(EntityFieldInfo info){
230         return new Predicate().addValue(info.getFullColumn(), ">=", info.getColumnFieldData().toString());
231     }
232 
233     Predicate lt(T)(EntityFieldInfo info, T t){
234         assertType!(T)(info);
235         return new Predicate().addValue(info.getFullColumn(), "<", quoteSqlfNeed(t));
236     }
237 
238     Predicate lt(EntityFieldInfo info){
239         return new Predicate().addValue(info.getFullColumn(), "<", info.getColumnFieldData().toString());
240     }
241 
242     Predicate le(T)(EntityFieldInfo info, T t){
243         assertType!(T)(info);
244         return new Predicate().addValue(info.getFullColumn(), "<=", quoteSqlfNeed(t));
245     }
246 
247     Predicate le(EntityFieldInfo info){
248         return new Predicate().addValue(info.getFullColumn(), "<=", info.getColumnFieldData().toString());
249     }
250 
251     Predicate like(EntityFieldInfo info, string pattern) {
252         return new Predicate().addValue(info.getFullColumn(), "like", quoteSqlfNeed(pattern));
253     }
254 
255     Predicate notLike(EntityFieldInfo info, string pattern) {
256         return new Predicate().addValue(info.getFullColumn(), "not like", quoteSqlfNeed(pattern));
257     }
258 
259     Predicate between(T)(EntityFieldInfo info, T t1, T t2) {
260         assertType!(T)(info);
261         return new Predicate().betweenValue(info.getFullColumn(), quoteSqlfNeed(t1), quoteSqlfNeed(t2));
262     }
263 
264     Predicate In(T...)(EntityFieldInfo info, T args) {
265         foreach(k,v; args) {
266             if (k == 0) {
267                 assertType!(typeof(v))(info);
268             }
269         }
270         return new Predicate().In(info.getFullColumn(), args);
271     }
272 
273     void assertType(T)(EntityFieldInfo info) {
274         if (info.getColumnFieldData().hasValue()) {
275             static if (is(T == string)) {
276                 if (info.getColumnFieldData().type != typeid(string)) {
277                     throw new EntityException("EntityFieldInfo %s type need been string not %s".format(info.getFieldName(), info.getColumnFieldData().type));
278                 }
279             }else {
280                 if (info.getColumnFieldData().type == typeid(string)) {
281                     throw new EntityException("EntityFieldInfo %s type need been number not string".format(info.getFieldName()));
282                 }
283             }
284         }
285         else {
286             throw new EntityException("EntityFieldInfo %s is object can not be Predicate".format(info.getFieldName()));
287         }   
288     }
289 
290     private string quoteSqlfNeed(T)(T t)
291     {
292         static if(is(T == string)) {
293             return escapeWithQuotes(t);
294         } else {
295             return t.to!string;
296         }
297     }
298 
299     string escapeWithQuotes(string str) {
300 
301         DatabaseOption options = _manager.getDbOption();
302 
303 		if(options.isPgsql()) {
304 			return PgUtil.escapeWithQuotes(str);
305 		} else if(options.isMysql) {
306             return MySQLUtil.escapeWithQuotes(str);
307         }
308 
309 		return str;
310 	}    
311 }