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.NativeQuery;
13 
14 import hunt.entity;
15 import hunt.collection.ArrayList;
16 import hunt.collection.List;
17 import hunt.logging;
18 import hunt.Number;
19 import hunt.String;
20 import hunt.Integer;
21 import hunt.Long;
22 import hunt.Double;
23 import hunt.Float;
24 import hunt.Short;
25 import hunt.Byte;
26 import hunt.Boolean;
27 import hunt.Nullable;
28 import hunt.entity.EntityException;
29 import hunt.sql.util.DBType;
30 import hunt.sql.SQLUtils;
31 
32 import std.regex;
33 import std.format;
34 
35 // version(WITH_HUNT_TRACE)
36 // {
37 //     import hunt.trace.Constrants;
38 //     import hunt.trace.Plugin;
39 //     import hunt.trace.Span;
40 // }
41 
42 import std.algorithm;
43 
44 deprecated("Using RowSet instead.")
45 alias ResultSet = RowSet;
46 
47 class NativeQuery
48 {
49 
50     private string _nativeSql;
51     private EntityManager _manager;
52     private Object[int] _params;
53     private Object[string] _parameters;
54     private int _lastInsertId = -1;
55     private int _affectRows = 0;
56 
57     // version (WITH_HUNT_TRACE)
58     // {
59     //     private Span _span;
60     //     private string[string] _tags;
61     // }
62 
63     this(EntityManager manager, string sql)
64     {
65         _manager = manager;
66         _nativeSql = sql;
67     }
68 
69     // version (WITH_HUNT_TRACE)
70     // {
71     //     private void beginTrace(string name)
72     //     {
73     //         _tags.clear();
74     //         _span = traceSpanBefore(name);
75     //     }
76 
77     //     private void endTrace(string error = null)
78     //     {
79     //         if (_span !is null)
80     //         {
81     //             traceSpanAfter(_span, _tags, error);
82     //         }
83     //     }
84     // }
85 
86     public RowSet getResultList()
87     {
88         auto sql = paramedSql();
89         // version (WITH_HUNT_TRACE)
90         // {
91         //     beginTrace("NativeQuery getResultList");
92         //     scope (exit)
93         //     {
94         //         _tags["sql"] = sql;
95         //         endTrace();
96         //     }
97         // }
98         auto stmt = _manager.getSession().prepare(sql);
99         return stmt.query();
100     }
101 
102     public int executeUpdate()
103     {
104         auto sql = paramedSql();
105         // version (WITH_HUNT_TRACE)
106         // {
107         //     beginTrace("NativeQuery executeUpdate");
108         //     scope (exit)
109         //     {
110         //         _tags["sql"] = sql;
111         //         endTrace();
112         //     }
113         // }
114         auto stmt = _manager.getSession().prepare(sql);
115         //TODO row line count is 0 for Update
116         _lastInsertId = stmt.lastInsertId();
117         _affectRows = stmt.affectedRows();
118         return stmt.execute();
119     }
120 
121     public int lastInsertId()
122     {
123         return _lastInsertId;
124     }
125 
126     public int affectedRows()
127     {
128         return _affectRows;
129     }
130 
131 
132     public void setParameter(R)(int idx, R param)
133     {
134         static if (is(R == int) || is(R == uint))
135         {
136             _params[idx] = new Integer(param);
137         }
138         else static if (is(R == char))
139         {
140             _params[idx] = new String(cast(string)[param]);
141         }
142         else static if (is(R == string))
143         {
144             _params[idx] = new String(param);
145         }
146         else static if(is(R == byte[]) || is(R == ubyte[])) {
147             _params[idx] = new Bytes(cast(byte[])param);
148         }
149         else static if (is(R == bool))
150         {
151             _params[idx] = new Boolean(param);
152         }
153         else static if (is(R == double))
154         {
155             _params[idx] = new Double(param);
156         }
157         else static if (is(R == float))
158         {
159             _params[idx] = new Float(param);
160         }
161         else static if (is(R == short) || is(R == ushort))
162         {
163             _params[idx] = new Short(param);
164         }
165         else static if (is(R == long) || is(R == ulong))
166         {
167             _params[idx] = new Long(param);
168         }
169         else static if (is(R == byte) || is(R == ubyte))
170         {
171             _params[idx] = new Byte(param);
172         }
173         else static if (is(R == class))
174         {
175             _params[key] = param;
176         }
177         else
178         {
179             throw new EntityException("IllegalArgument not support : " ~ R.stringof);
180         }
181     }
182 
183     public void setParameter(R)(string key, R param)
184     {
185         static if (is(R == int) || is(R == uint))
186         {
187             _parameters[key] = new Integer(param);
188         }
189         else static if (is(R == string) || is(R == char) || is(R == byte[]))
190         {
191             _parameters[key] = new String(param);
192         }
193         else static if (is(R == bool))
194         {
195             _parameters[key] = new Boolean(param);
196         }
197         else static if (is(R == double))
198         {
199             _parameters[key] = new Double(param);
200         }
201         else static if (is(R == float))
202         {
203             _parameters[key] = new Float(param);
204         }
205         else static if (is(R == short) || is(R == ushort))
206         {
207             _parameters[key] = new Short(param);
208         }
209         else static if (is(R == long) || is(R == ulong))
210         {
211             _parameters[key] = new Long(param);
212         }
213         else static if (is(R == byte) || is(R == ubyte))
214         {
215             _parameters[key] = new Byte(param);
216         }
217         else static if (is(R == class))
218         {
219             _parameters[key] = param;
220         }
221         else
222         {
223             throw new EntityException("IllegalArgument not support : " ~ R.stringof);
224         }
225     }
226 
227     private string paramedSql()
228     {
229         version(HUNT_DEBUG) info(_nativeSql);
230 
231         string str = _nativeSql;
232         foreach (k, v; _parameters)
233         {
234             auto re = regex(r":" ~ k ~ r"([^\w])", "g");
235             if ((cast(String) v !is null) || (cast(Nullable!string) v !is null))
236             {
237                 str = str.replaceAll(re, quoteSqlString(v.toString()) ~ "$1");
238             }
239             else
240             {
241                 str = str.replaceAll(re, v.toString() ~ "$1");
242             }
243         }
244 
245 
246         if (_params.length > 0)
247         {
248             auto keys = _params.keys;
249             sort!("a < b")(keys);
250             List!Object params = new ArrayList!Object();
251             foreach (e; keys)
252             {
253                 params.add(_params[e]);
254             }
255 
256             auto opt = _manager.getDbOption();
257             string dbtype;
258             if (opt.isMysql())
259             {
260                 dbtype = DBType.MYSQL.name;
261             }
262             else if (opt.isPgsql())
263             {
264                 dbtype = DBType.POSTGRESQL.name;
265             }
266             // else if (opt.isSqlite())
267             // {
268             //     dbtype = DBType.SQLITE.name;
269 
270             // }
271             else
272             {
273                 throw new Exception("not support dbtype : %s".format(opt.schemeName()));
274             }
275                 str = SQLUtils.format(str, DBType.MYSQL.name, params);
276             }
277 
278         version(HUNT_DEBUG) info(str);
279         return str;
280     }
281 
282 }