1 /*
2  * Copyright (C) 2019, HuntLabs
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 module hunt.database.base.impl.PreparedQueryImpl;
19 
20 import hunt.database.base.impl.ArrayTuple;
21 import hunt.database.base.impl.Connection;
22 import hunt.database.base.impl.CursorImpl;
23 import hunt.database.base.impl.PreparedStatement;
24 import hunt.database.base.impl.QueryResultHandler;
25 import hunt.database.base.impl.RowSetImpl;
26 import hunt.database.base.impl.SqlResultBuilder;
27 
28 import hunt.database.base.impl.command.CloseCursorCommand;
29 import hunt.database.base.impl.command.CloseStatementCommand;
30 import hunt.database.base.impl.command.CommandResponse;
31 import hunt.database.base.impl.command.ExtendedBatchQueryCommand;
32 import hunt.database.base.impl.command.ExtendedQueryCommand;
33 
34 import hunt.database.base.AsyncResult;
35 import hunt.database.base.Common;
36 import hunt.database.base.Cursor;
37 import hunt.database.base.Exceptions;
38 import hunt.database.base.PreparedQuery;
39 import hunt.database.base.SqlResult;
40 import hunt.database.base.RowSet;
41 import hunt.database.base.RowStream;
42 import hunt.database.base.Row;
43 import hunt.database.base.Tuple;
44 
45 import hunt.collection.List;
46 import hunt.Exceptions;
47 import hunt.Functions;
48 import hunt.logging;
49 import hunt.Object;
50 
51 import core.atomic;
52 import std.array;
53 import std.variant;
54 
55 /**
56  * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
57  */
58 class PreparedQueryImpl : PreparedQuery {
59 
60     private DbConnection conn;
61     private PreparedStatement ps;
62     private shared bool closed = false;
63 
64     this(DbConnection conn, PreparedStatement ps) { 
65         this.conn = conn;
66         this.ps = ps;
67     }
68     
69     PreparedStatement getPreparedStatement() {
70         return ps;
71     }
72 
73     PreparedQuery execute(RowSetHandler handler) {
74         return execute(ArrayTuple.EMPTY, handler);
75     }
76 
77     override
78     PreparedQuery execute(Tuple args, RowSetHandler handler) {
79         return execute!(RowSet, RowSetImpl, RowSet)(args, false, RowSetImpl.FACTORY, handler);
80     }
81 
82     // <R1, R2 extends SqlResultBase!(R1, R2), R3 extends SqlResult!(R1)> 
83     private PreparedQuery execute(R1, R2, R3)(
84             Tuple args, bool singleton,
85             Function!(R1, R2) factory,
86             AsyncResultHandler!(R3) handler) {
87 
88         SqlResultBuilder!(R1, R2, R3) b = new SqlResultBuilder!(R1, R2, R3)(factory, handler);
89         return execute!(R1)(args, 
90             0, "", false, singleton, b, 
91             (CommandResponse!bool r) {  b.handle(r); }
92         );
93     }
94 
95     PreparedQuery execute(R)(Tuple args,
96             int fetch,
97             string cursorId,
98             bool suspended,
99             bool singleton,
100             QueryResultHandler!(R) resultHandler,
101             ResponseHandler!(bool) handler) {
102 
103         string msg = ps.prepare(cast(List!(Variant)) args);
104         if (!msg.empty()) {
105             version(HUNT_DB_DEBUG) warning(msg);
106             handler(failedResponse!(bool)(new DatabaseException(msg)));
107         } else {
108             ExtendedQueryCommand!R cmd = new ExtendedQueryCommand!R(
109                 ps,
110                 args,
111                 fetch,
112                 cursorId,
113                 suspended,
114                 singleton,
115                 resultHandler);
116             cmd.handler = handler;
117             conn.schedule(cmd);
118         }
119    
120         return this;
121     }
122 
123     Cursor cursor() {
124         return cursor(ArrayTuple.EMPTY);
125     }
126 
127     // override
128     Cursor cursor(Tuple args) {
129         string msg = ps.prepare(cast(List!(Variant)) args);
130         if (msg !is null) {
131             throw new IllegalArgumentException(msg);
132         }
133         return new CursorImpl(this, args);
134     }
135 
136     // override
137     void close() {
138 
139         warning("do nothing");
140         
141         // close(ar -> {
142         // });
143     }
144 
145     PreparedQuery batch(List!(Tuple) argsList, RowSetHandler handler) {
146         // return batch(argsList, false, RowSetImpl.FACTORY, RowSetImpl.COLLECTOR, handler);
147         implementationMissing(false);
148         return null;
149     }
150 
151     // override
152     // <R> PreparedQuery batch(List!(Tuple) argsList, Collector<Row, ?, R> collector, Handler!(AsyncResult!(SqlResult!(R))) handler) {
153     //     return batch(argsList, true, SqlResultImpl::new, collector, handler);
154     // }
155 
156     // private <R1, R2 extends SqlResultBase!(R1, R2), R3 extends SqlResult!(R1)> PreparedQuery batch(
157     //     List!(Tuple) argsList,
158     //     bool singleton,
159     //     Function!(R1, R2) factory,
160     //     Collector<Row, ?, R1> collector,
161     //     Handler!(AsyncResult!(R3)) handler) {
162     //     for  (Tuple args : argsList) {
163     //         string msg = ps.prepare((List!(Object)) args);
164     //         if (msg !is null) {
165     //             handler.handle(Future.failedFuture(msg));
166     //             return this;
167     //         }
168     //     }
169     //     SqlResultBuilder!(R1, R2, R3) b = new SqlResultBuilder<>(factory, handler);
170     //     ExtendedBatchQueryCommand cmd = new ExtendedBatchQueryCommand<>(ps, argsList, singleton, collector, b);
171     //     cmd.handler = b;
172     //     conn.schedule(cmd);
173     //     return this;
174     // }
175 
176     // override
177     // RowStream!(Row) createStream(int fetch, Tuple args) {
178     //     return new RowStreamImpl(this, fetch, args);
179     // }
180 
181     override
182     void close(AsyncVoidHandler handler) {
183         version(HUNT_DB_DEBUG) infof("closed: %s", closed);
184         if(cas(&closed, false, true)) {
185             CloseStatementCommand cmd = new CloseStatementCommand(ps);
186             cmd.handler = (h) { 
187                 if(handler !is null) {
188                     handler(h); 
189                 }
190             };
191             conn.schedule(cmd);
192         } else if(handler !is null) {
193             handler(failedResult!(Void)(new DatabaseException("Already closed")));
194         }
195     }
196 
197     void closeCursor(string cursorId, AsyncVoidHandler handler) {
198         version(HUNT_DB_DEBUG) infof("cursorId: %s", cursorId);
199         CloseCursorCommand cmd = new CloseCursorCommand(cursorId, ps);
200         cmd.handler = (h) { if(handler !is null) handler(h); };
201         conn.schedule(cmd);
202     }
203 }