DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
LocalQueryResult.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.Globalization;
20 
21 using Deveel.Data.Client;
22 using Deveel.Data.Sql.Objects;
23 
24 namespace Deveel.Data.Protocol {
25  class LocalQueryResult : IDisposable {
27 
28  // private static int uniqueIdKey = 1;
29 
30  // private int uniqueId;
32  // private int queryTimeMs;
33  private int resultRowCount;
34 
35  private int maxRowCount = Int32.MaxValue;
36 
37  private int blockTopRow;
38  private int blockRowCount;
39  private int fetchSize;
40 
41  private Dictionary<string, int> columnHash;
42 
43  private int realIndex = Int32.MaxValue;
44  private int realIndexOffset = -1;
45 
47 
48  public LocalQueryResult(DeveelDbConnection connection) {
49  this.connection = connection;
50 
51  MaxFetchSize = connection.Settings.MaxFetchSize;
52  DefaultFetchSize = connection.Settings.FetchSize;
53  }
54 
56  Dispose(false);
57  }
58 
59  public bool VerboseColumnNames {
60  get { return connection.Settings.VerboseColumnNames; }
61  }
62 
63  public int MaxFetchSize { get; private set; }
64 
65  public int DefaultFetchSize { get; private set; }
66 
67  public bool Closed { get; private set; }
68 
69  public int QueryTime { get; internal set; }
70 
79  public int ResultId { get; private set; }
80 
84  public int RowCount {
85  get {
86  // The row count is whatever is the least between max_row_count (the
87  // maximum the user has set) and result_row_count (the actual number of
88  // rows in the result.
89  return System.Math.Min(resultRowCount, maxRowCount);
90  }
91  }
92 
96  public int ColumnCount {
97  get { return columns.Length; }
98  }
99 
100  public bool HasLargeObject {
101  get {
102  // TODO:
103  return false;
104  }
105  }
106 
120  public bool IsUpdate {
121  get {
122  // Must have 1 col and 1 row and the title of the column must be
123  // 'result' aliased.
124  return (ColumnCount == 1 && RowCount == 1 &&
125  columns[0].Name.Equals("@aresult"));
126  }
127  }
128 
136  private void EnsureIndexLoaded() {
137  if (realIndex == -1)
138  throw new DeveelDbException("Row index out of bounds.");
139 
140  // Offset into our block
141  int rowOffset = realIndex - blockTopRow;
142  if (rowOffset >= blockRowCount) {
143  // Need to download the next block from the server.
144  Download(realIndex, fetchSize);
145  // Set up the index into the downloaded block.
146  rowOffset = realIndex - blockTopRow;
147  realIndexOffset = rowOffset;
148  } else if (rowOffset < 0) {
149  int fsDif = System.Math.Min(fetchSize, 8);
150  // Need to download the next block from the server.
151  Download(realIndex - fetchSize + fsDif, fetchSize);
152  // Set up the index into the downloaded block.
153  rowOffset = realIndex - blockTopRow;
154  realIndexOffset = rowOffset;
155  }
156  }
157 
169  public int FindColumnIndex(string name) {
170  // For speed, we keep column name -> column index mapping in the hashtable.
171  // This makes column reference by string faster.
172  if (columnHash == null) {
173  var comparer = connection.Settings.IgnoreIdentifiersCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
174  columnHash = new Dictionary<string, int>(comparer);
175  }
176 
177  int index;
178  if (!columnHash.TryGetValue(name, out index)) {
179  int colCount = ColumnCount;
180  // First construct an unquoted list of all column names
181  String[] cols = new String[colCount];
182  for (int i = 0; i < colCount; ++i) {
183  String colName = columns[i].Name;
184  if (colName.StartsWith("\"")) {
185  colName = colName.Substring(1, colName.Length - 2);
186  }
187  // Strip any codes from the name
188  if (colName.StartsWith("@")) {
189  colName = colName.Substring(2);
190  }
191 
192  cols[i] = colName;
193  }
194 
195  for (int i = 0; i < colCount; ++i) {
196  String colName = cols[i];
197  if (colName.Equals(name)) {
198  columnHash[name] = i ;
199  return i;
200  }
201  }
202 
203  // If not found then search for column name ending,
204  string pointName = "." + name;
205  for (int i = 0; i < colCount; ++i) {
206  String colName = cols[i];
207  if (colName.EndsWith(pointName)) {
208  columnHash[name] = i;
209  return i;
210  }
211  }
212 
213  throw new DeveelDbException("Couldn't find column with name: " + name);
214  }
215 
216  return index;
217  }
218 
219  public object GetRuntimeValue(int ordinal) {
220  var value = GetRawColumn(ordinal);
221 
222  if (value == null || value.IsNull)
223  return DBNull.Value;
224 
225  var destType = GetColumn(ordinal).RuntimeType;
226 
227  return Convert.ChangeType(value, destType, CultureInfo.InvariantCulture);
228  }
229 
230  public ISqlObject GetRawColumn(int column) {
231  // ASSERTION -
232  // Is the given column in bounds?
233  if (column < 0 || column >= ColumnCount)
234  throw new IndexOutOfRangeException("Column index out of bounds: 1 > " + column + " > " + ColumnCount);
235 
236  // Ensure the current indexed row is fetched from the server.
237  EnsureIndexLoaded();
238 
239  var row = resultBlock.GetRow(realIndexOffset);
240  return row.Values[column];
241  }
242 
243  public int AffectedRows {
244  get {
245  if (!IsUpdate)
246  throw new DeveelDbException("Unable to format command result as an update value.");
247 
248  var row = resultBlock.GetRow(0);
249  ISqlObject ob = row.Values[0];
250  if (ob is SqlNumber)
251  return ((SqlNumber) ob).ToInt32();
252 
253  return 0;
254  }
255  }
256 
257  public void Setup(int id, QueryResultColumn[] columnList, int totalRowCount) {
258  ResultId = id;
259  columns = columnList;
260  resultRowCount = totalRowCount;
261  blockTopRow = -1;
262  resultBlock = null;
263 
264  realIndex = -1;
265  fetchSize = connection.Settings.FetchSize;
266  Closed = false;
267  }
268 
269  public void SetFetchSize(int rows) {
270  fetchSize = rows > 0 ? System.Math.Min(rows, MaxFetchSize) : DefaultFetchSize;
271  }
272 
273  public void SetMaxRowCount(int rowCount) {
274  maxRowCount = rowCount == 0 ? Int32.MaxValue : rowCount;
275  }
276 
277  public void DownloadAndClose() {
278  Download(0, resultRowCount);
279  connection.DisposeResult(ResultId);
280  Closed = true;
281  }
282 
283  public void Download(int rowIndex, int rowCount) {
284  // If row_count is 0 then we don't need to do anything.
285  if (rowCount == 0)
286  return;
287 
288  if (rowIndex + rowCount < 0)
289  throw new DeveelDbException("Result row index is before the start of the set.");
290 
291  if (rowIndex < 0) {
292  rowIndex = 0;
293  rowCount = rowCount + rowIndex;
294  }
295 
296  if (rowIndex >= RowCount)
297  throw new DeveelDbException("Result row index is after the end of the set.");
298  if (rowIndex + rowCount > RowCount)
299  rowCount = RowCount - rowIndex;
300 
301  if (ResultId == -1)
302  throw new DeveelDbException("Result ID is invalid.");
303 
304  try {
305 
306  // Request the result via the RowCache. If the information is not found
307  // in the row cache then the request is forwarded onto the database.
308  resultBlock = connection.RowCache.GetResultPart(ResultId, rowIndex, rowCount, ColumnCount, RowCount);
309 
310  // Set the row that's at the top
311  blockTopRow = rowIndex;
312 
313  // Set the number of rows in the block.
314  blockRowCount = rowCount;
315  } catch(DeveelDbException) {
316  throw;
317  } catch (Exception ex) {
318  throw new DeveelDbException("Error while downloading the result data.", ex);
319  }
320  }
321 
322  public QueryResultColumn GetColumn(int offset) {
323  return columns[offset];
324  }
325 
326  private void RealIndexUpdate() {
327  // Set up the index into the downloaded block.
328  int rowOffset = realIndex - blockTopRow;
329  realIndexOffset = rowOffset;
330  }
331 
332  public bool First() {
333  realIndex = 0;
334  RealIndexUpdate();
335  return realIndex < RowCount;
336  }
337 
338  public bool Next() {
339  int rowCount = RowCount;
340  if (realIndex < rowCount) {
341  ++realIndex;
342  if (realIndex < rowCount) {
343  RealIndexUpdate();
344  }
345  }
346  return (realIndex < rowCount);
347  }
348 
349  public void Close() {
350  if (ResultId != -1) {
351  if (!Closed) {
352  // Request to close the current result set
353  connection.DisposeResult(ResultId);
354  Closed = true;
355  }
356 
357  ResultId = -1;
358  realIndex = Int32.MaxValue;
359 
360  // Clear the column name -> index mapping,
361  if (columnHash != null) {
362  columnHash.Clear();
363  }
364  }
365  }
366 
367  public void Dispose() {
368  Dispose(true);
369  GC.SuppressFinalize(this);
370  }
371 
372  private void Dispose(bool disposing) {
373  if (disposing) {
374  try {
375  Close();
376  } catch (DeveelDbException) {
377  // Ignore
378  // We ignore exceptions because handling cases where the server
379  // connection has broken for many ResultSets would be annoying.
380  }
381  }
382 
383  connection = null;
384  columns = null;
385  resultBlock = null;
386  }
387  }
388 }
int FindColumnIndex(string name)
Searches for the index of the column with the given name.
QueryResultRow GetRow(int index)
Dictionary< string, int > columnHash
void EnsureIndexLoaded()
Ensures that the row index pointed to by 'real_index' is actually loaded into the 'result_block'...
Defines the contract for a valid SQL Object
Definition: ISqlObject.cs:23
string Name
Returns the name of the field.
void Download(int rowIndex, int rowCount)
LocalQueryResult(DeveelDbConnection connection)
void Setup(int id, QueryResultColumn[] columnList, int totalRowCount)
DeveelDbConnectionStringBuilder Settings
QueryResultColumn GetColumn(int offset)
QueryResultPart GetResultPart(int resultId, int rowIndex, int rowCount, int colCount, int totalRowCount)
Requests a block of parts.