DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
DeveelDbCommand.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
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 using System;
18 using System.Collections.Generic;
19 using System.Data;
20 using System.Data.Common;
21 using System.IO;
22 
23 using Deveel.Data.Protocol;
24 using Deveel.Data.Sql;
25 using Deveel.Data.Sql.Objects;
26 using Deveel.Data.Types;
27 
28 using SysParameterDirection = System.Data.ParameterDirection;
29 
30 namespace Deveel.Data.Client {
31  public sealed class DeveelDbCommand : DbCommand {
32  private int? timeout;
35 
36  private bool prepared;
37  private List<QueryParameter> preparedParameters;
38 
40 
41  private int resultIndex;
43 
44  public DeveelDbCommand()
45  : this((DeveelDbConnection)null) {
46  }
47 
49  : this(connection, null) {
50  }
51 
52  public DeveelDbCommand(string commandText)
53  : this(null, commandText) {
54  }
55 
56  public DeveelDbCommand(DeveelDbConnection connection, string commandText) {
57  Connection = connection;
58  CommandText = commandText;
59 
60  parameters = new DeveelDbParameterCollection(this);
61  }
62 
64  if (direction == SysParameterDirection.Input)
65  return QueryParameterDirection.In;
66  if (direction == SysParameterDirection.Output)
67  return QueryParameterDirection.Out;
68  if (direction == SysParameterDirection.InputOutput)
69  return QueryParameterDirection.InOut;
70 
71  throw new NotSupportedException();
72  }
73 
75  // TODO: If we have a Value that is a Stream object, upload it and get
76  // back the object ID to replace the value
77 
78  var name = parameter.ParameterName;
79  if (String.IsNullOrEmpty(name))
80  name = QueryParameter.Marker;
81 
82  var meta = new [] {
83  new DataTypeMeta("MaxSize", parameter.Size.ToString()),
84  new DataTypeMeta("Precision", parameter.Precision.ToString()),
85  new DataTypeMeta("Scale", parameter.Scale.ToString()),
86  new DataTypeMeta("Locale", parameter.Locale)
87  };
88 
89  var dataType = SqlType.Resolve(parameter.SqlType, meta);
90  var value = dataType.CreateFrom(parameter.Value);
91 
92  var queryParameter = new QueryParameter(name, dataType, value);
93  queryParameter.Direction = GetParamDirection(parameter.Direction);
94  return queryParameter;
95  }
96 
97  private void ExecuteQuery() {
98  var query = CreateQuery();
99 
100  int commitId = Transaction != null ? Transaction.CommitId : -1;
101  var response = connection.ExecuteQuery(commitId, query);
102  CreateResults(response);
103  }
104 
105  private void CreateResults(IQueryResponse[] response) {
106  results = new LocalQueryResult[response.Length];
107 
108  for (int i = 0; i < response.Length; i++) {
109  var r = response[i];
110  var columns = new QueryResultColumn[r.ColumnCount];
111  for (int j = 0; j < columns.Length; j++) {
112  columns[j] = r.GetColumn(j);
113  }
114 
115  var result = new LocalQueryResult(connection);
116  result.QueryTime = r.QueryTimeMillis;
117 
118  result.Setup(r.ResultId, columns, r.RowCount);
119  result.SetFetchSize(connection.Settings.FetchSize);
120  result.SetMaxRowCount(connection.Settings.MaxFetchSize);
121 
122  // Does the result set contain large objects? We can't cache a
123  // result that contains binary data.
124 
125  bool hasLargeObject = result.HasLargeObject;
126 
127  // If the result row count < 40 then download and store locally in the
128  // result set and dispose the resources on the server.
129  if (!hasLargeObject && result.RowCount < 40) {
130  result.DownloadAndClose();
131  } else {
132  result.Download(0, System.Math.Min(10, result.RowCount));
133  }
134 
135  results[i] = result;
136  }
137  }
138 
139  private SqlQuery CreateQuery() {
140  QueryParameter[] queryParameters;
141  if (prepared && preparedParameters != null) {
142  queryParameters = preparedParameters.ToArray();
143  } else {
144  queryParameters = new QueryParameter[parameters.Count];
145  for (int i = 0; i < parameters.Count; i++) {
146  var parameter = parameters[i];
147  var queryParam = PrepareParameter(parameter);
148  queryParameters[i] = queryParam;
149  }
150  }
151 
152  var query = new SqlQuery(CommandText);
153 
154  // now verify all parameter names are consistent
155  foreach (var parameter in queryParameters) {
156  if (connection.Settings.ParameterStyle == QueryParameterStyle.Marker) {
157  if (!String.IsNullOrEmpty(parameter.Name) &&
158  parameter.Name != QueryParameter.Marker)
159  throw new InvalidOperationException();
160  } else if (connection.Settings.ParameterStyle == QueryParameterStyle.Named) {
161  if (String.IsNullOrEmpty(parameter.Name))
162  throw new InvalidOperationException("Named parameters must have a name defined.");
163 
164  if (parameter.Name == QueryParameter.Marker)
165  throw new InvalidOperationException();
166  if (parameter.Name.Length <= 1)
167  throw new InvalidOperationException();
168  if (!Char.IsLetter(parameter.Name[0]) &&
169  parameter.Name[0] != QueryParameter.NamePrefix)
170  throw new InvalidOperationException();
171  }
172 
173  query.Parameters.Add(parameter);
174  }
175 
176  return query;
177  }
178 
179  public override void Prepare() {
180  if (!prepared) {
181  try {
182  PrepareCommand();
183  } finally {
184  prepared = true;
185  }
186  }
187  }
188 
189  private void PrepareCommand() {
190 
191  }
192 
193  internal LocalQueryResult CurrentResult {
194  get {
195  if (results != null) {
196  if (resultIndex < results.Length) {
197  return results[resultIndex];
198  }
199  }
200  return null;
201  }
202  }
203 
204  public override string CommandText { get; set; }
205 
206  public override int CommandTimeout {
207  get {
208  if (timeout == null && connection != null)
209  return connection.Settings.QueryTimeout;
210  if (timeout == null)
211  return -1;
212 
213  return timeout.Value;
214  }
215  set { timeout = value; }
216  }
217 
218  public override CommandType CommandType { get; set; }
219 
220  public override UpdateRowSource UpdatedRowSource { get; set; }
221 
222  protected override DbConnection DbConnection {
223  get { return Connection; }
224  set { Connection = (DeveelDbConnection) value; }
225  }
226 
227  public new DeveelDbConnection Connection {
228  get { return connection; }
229  set { connection = value; }
230  }
231 
232  protected override DbParameterCollection DbParameterCollection {
233  get { return Parameters; }
234  }
235 
236  public new DeveelDbParameterCollection Parameters {
237  get { return parameters; }
238  }
239 
240  protected override DbTransaction DbTransaction {
241  get { return Transaction; }
242  set { Transaction = (DeveelDbTransaction) value; }
243  }
244 
245  public new DeveelDbTransaction Transaction {
246  get { return transaction; }
247  set {
248  if (value == null && transaction != null)
249  transaction = null;
250  else if (transaction != null &&
251  (value != null && value.CommitId!= transaction.CommitId))
252  throw new ArgumentException("The command is already bound to another transaction.");
253 
254  transaction = value;
255  }
256  }
257 
258  public override bool DesignTimeVisible { get; set; }
259 
260  public override void Cancel() {
261  try {
262  if (results != null) {
263  foreach (var result in results) {
264  connection.DisposeResult(result.ResultId);
265  }
266  }
267  } finally {
268  connection.EndState();
269  }
270  }
271 
272  internal bool NextResult() {
273  // If we are at the end then return false
274  if (results == null ||
275  resultIndex + 1 >= results.Length) {
276  return false;
277  }
278 
279  // Move to the next result set.
280  ++resultIndex;
281 
282  // We successfully moved to the next result
283  return true;
284  }
285 
286  protected override DbParameter CreateDbParameter() {
287  return CreateParameter();
288  }
289 
291  return new DeveelDbParameter();
292  }
293 
294  private void AssertConnectionOpen() {
295  if (Connection == null)
296  throw new DeveelDbException("The command is not associated to any connection.");
297 
298  if (Connection.State == ConnectionState.Closed) {
299  try {
300  Connection.Open();
301  } catch (DeveelDbException) {
302  throw;
303  } catch (Exception ex) {
304  throw new DeveelDbException("Failed to open the underlying connection", ex);
305  }
306  }
307  }
308 
309  protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) {
310  return ExecuteReader(behavior);
311  }
312 
313  public new DeveelDbDataReader ExecuteReader(CommandBehavior behavior) {
314  try {
315  AssertConnectionOpen();
316 
317  if (connection.State == ConnectionState.Fetching)
318  throw new InvalidOperationException("Another reader is already open for the connection.");
319 
320  if (connection.State != ConnectionState.Open)
321  throw new InvalidOperationException("The connection is not open.");
322 
323  ExecuteQuery();
324 
325  return new DeveelDbDataReader(this, behavior);
326  } catch (Exception ex) {
327  throw new DeveelDbException("An error occurred when executing the reader.", ex);
328  }
329  }
330 
332  return ExecuteReader(CommandBehavior.Default);
333  }
334 
335  public override int ExecuteNonQuery() {
336  try {
337  AssertConnectionOpen();
338 
339  connection.ChangeState(ConnectionState.Executing);
340 
341  ExecuteQuery();
342 
343  if (results == null || results.Length == 0)
344  return -1;
345 
346  var result = results[0];
347  if (!result.IsUpdate)
348  return -1;
349 
350  return result.AffectedRows;
351  } catch (Exception ex) {
352  throw new DeveelDbException("An error occurred while executing the non-query command.", ex);
353  } finally {
354  connection.EndState();
355  }
356  }
357 
358  public override object ExecuteScalar() {
359  try {
360  AssertConnectionOpen();
361 
362  connection.ChangeState(ConnectionState.Executing);
363 
364  ExecuteQuery();
365 
366  if (results == null || results.Length == 0)
367  return null;
368 
369  var result = results[0];
370  if (!result.First())
371  return null;
372 
373  if (result.RowCount == 0)
374  return null;
375 
376  return result.GetRuntimeValue(0);
377  } catch (Exception ex) {
378  throw new DeveelDbException("Error when selecting a scalar value.", ex);
379  } finally {
380  connection.EndState();
381  }
382  }
383 
384  protected override void Dispose(bool disposing) {
385  if (disposing) {
386  if (results != null) {
387  foreach (var result in results) {
388  result.Dispose();
389  }
390  }
391 
392  if (parameters != null) {
393  foreach (IDbDataParameter parameter in parameters) {
394  if (parameter.Value is IDisposable) {
395  try {
396  ((IDisposable)parameter.Value).Dispose();
397  } catch (Exception) {
398  }
399  }
400  }
401 
402  parameters.Clear();
403  }
404 
405  if (preparedParameters != null) {
406  foreach (var parameter in preparedParameters) {
407  if (parameter.Value is IDisposable) {
408  try {
409  ((IDisposable)parameter.Value).Dispose();
410  } catch (Exception) {
411  }
412  }
413  }
414  }
415  }
416 
417  preparedParameters = null;
418  parameters = null;
419  results = null;
420 
421  base.Dispose(disposing);
422  }
423  }
424 }
DeveelDbCommand(DeveelDbConnection connection)
new DeveelDbDataReader ExecuteReader()
A long string in the system.
QueryParameterDirection GetParamDirection(SysParameterDirection direction)
new DeveelDbParameter CreateParameter()
void ChangeState(ConnectionState newState)
override void Dispose(bool disposing)
QueryParameter PrepareParameter(DeveelDbParameter parameter)
The response to a command executed via the IDatabaseInterface.ExecuteQuery method in the IDatabaseInt...
List< QueryParameter > preparedParameters
new DeveelDbDataReader ExecuteReader(CommandBehavior behavior)
void CreateResults(IQueryResponse[] response)
QueryParameterStyle
In a SQL query object, this is the form of parameters passed from the client side to the server side...
Defines the properties of a specific SQL Type and handles the values compatible.
Definition: SqlType.cs:33
override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
DeveelDbConnectionStringBuilder Settings
override DbParameter CreateDbParameter()
DeveelDbParameterCollection parameters
static SqlType Resolve(SqlTypeCode typeCode)
Definition: SqlType.cs:441
virtual ISqlObject CreateFrom(object value)
Definition: SqlType.cs:437
DeveelDbCommand(DeveelDbConnection connection, string commandText)
IQToolkit.Data.Common.QueryParameter QueryParameter
IQueryResponse[] ExecuteQuery(int commitId, SqlQuery query)