Source code: org/hsqldb/Result.java
1 /* Copyrights and Licenses
2 *
3 * This product includes Hypersonic SQL.
4 * Originally developed by Thomas Mueller and the Hypersonic SQL Group.
5 *
6 * Copyright (c) 1995-2000 by the Hypersonic SQL Group. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 * - Redistributions of source code must retain the above copyright notice, this list of conditions
10 * and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright notice, this list of
12 * conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 * - All advertising materials mentioning features or use of this software must display the
15 * following acknowledgment: "This product includes Hypersonic SQL."
16 * - Products derived from this software may not be called "Hypersonic SQL" nor may
17 * "Hypersonic SQL" appear in their names without prior written permission of the
18 * Hypersonic SQL Group.
19 * - Redistributions of any form whatsoever must retain the following acknowledgment: "This
20 * product includes Hypersonic SQL."
21 * This software is provided "as is" and any expressed or implied warranties, including, but
22 * not limited to, the implied warranties of merchantability and fitness for a particular purpose are
23 * disclaimed. In no event shall the Hypersonic SQL Group or its contributors be liable for any
24 * direct, indirect, incidental, special, exemplary, or consequential damages (including, but
25 * not limited to, procurement of substitute goods or services; loss of use, data, or profits;
26 * or business interruption). However caused any on any theory of liability, whether in contract,
27 * strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this
28 * software, even if advised of the possibility of such damage.
29 * This software consists of voluntary contributions made by many individuals on behalf of the
30 * Hypersonic SQL Group.
31 *
32 *
33 * For work added by the HSQL Development Group:
34 *
35 * Copyright (c) 2001-2002, The HSQL Development Group
36 * All rights reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions are met:
40 *
41 * Redistributions of source code must retain the above copyright notice, this
42 * list of conditions and the following disclaimer, including earlier
43 * license statements (above) and comply with all above license conditions.
44 *
45 * Redistributions in binary form must reproduce the above copyright notice,
46 * this list of conditions and the following disclaimer in the documentation
47 * and/or other materials provided with the distribution, including earlier
48 * license statements (above) and comply with all above license conditions.
49 *
50 * Neither the name of the HSQL Development Group nor the names of its
51 * contributors may be used to endorse or promote products derived from this
52 * software without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
55 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
58 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
59 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
60 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
61 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
62 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
63 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65 */
66
67
68 package org.hsqldb;
69
70 import java.io.IOException;
71 import java.sql.SQLException;
72
73 // fredt@users 20020130 - patch 1.7.0 by fredt
74 // to ensure consistency of r.rTail r.iSize in all operations
75 // methods for set operations moved here from Select.java
76 // fredt@users 20020130 - patch 1.7.0 by fredt
77 // rewrite of LIMIT n m to apply to each select statement separately
78 // tony_lai@users 20020820 - patch 595073 by tlai@users - duplicated exception msg
79
80 /**
81 * Class declaration
82 *
83 * @version 1.7.0
84 */
85 class Result {
86
87 private Record rTail;
88 private int iSize;
89 private int iColumnCount;
90 static final int UPDATECOUNT = 0;
91 static final int ERROR = 1;
92 static final int DATA = 2;
93 int iMode;
94 String sError;
95 int errorCode;
96 int iUpdateCount;
97 Record rRoot;
98 String sLabel[];
99 String sTable[];
100 String sName[];
101 boolean isLabelQuoted[];
102 int colType[];
103 int colSize[];
104 int colScale[];
105
106 /**
107 * Constructor declaration
108 */
109 Result() {
110 iMode = UPDATECOUNT;
111 iUpdateCount = 0;
112 }
113
114 // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
115
116 /**
117 * Constructor declaration
118 *
119 * @param error
120 * @param code Description of the Parameter
121 */
122 Result(String error, int code) {
123
124 iMode = ERROR;
125 sError = error;
126 errorCode = code;
127 }
128
129 /**
130 * Constructor declaration
131 *
132 * @param columns
133 */
134 Result(int columns) {
135
136 prepareData(columns);
137
138 iColumnCount = columns;
139 }
140
141 /**
142 * Constructor declaration
143 *
144 * @param b
145 * @exception SQLException Description of the Exception
146 */
147 Result(byte b[]) throws SQLException {
148
149 try {
150 DatabaseRowInputInterface in = new BinaryServerRowInput(b);
151
152 iMode = in.readIntData();
153
154 if (iMode == ERROR) {
155
156 // tony_lai@users 20020820 - patch 595073
157 int code = in.readIntData();
158
159 throw Trace.getError(in.readString(), code);
160
161 // throw Trace.getError(in.readIntData(), in.readString());
162 } else if (iMode == UPDATECOUNT) {
163 iUpdateCount = in.readIntData();
164 } else if (iMode == DATA) {
165 int l = in.readIntData();
166
167 prepareData(l);
168
169 iColumnCount = l;
170
171 for (int i = 0; i < l; i++) {
172 colType[i] = in.readType();
173 sLabel[i] = in.readString();
174 sTable[i] = in.readString();
175 sName[i] = in.readString();
176 }
177
178 while (in.available() != 0) {
179 add(in.readData(colType));
180 }
181 }
182 } catch (IOException e) {
183 throw Trace.error(Trace.TRANSFER_CORRUPTED);
184 }
185 }
186
187 /**
188 * Method declaration
189 *
190 * @return
191 */
192 int getSize() {
193 return iSize;
194 }
195
196 /**
197 * Method declaration
198 *
199 * @param columns
200 */
201 void setColumnCount(int columns) {
202 iColumnCount = columns;
203 }
204
205 /**
206 * Method declaration
207 *
208 * @return
209 */
210 int getColumnCount() {
211 return iColumnCount;
212 }
213
214 /**
215 * Method declaration
216 *
217 * @param a
218 */
219 void append(Result a) {
220
221 if (rRoot == null) {
222 rRoot = a.rRoot;
223 } else {
224 rTail.next = a.rRoot;
225 }
226
227 rTail = a.rTail;
228 iSize += a.iSize;
229 }
230
231 /**
232 * Method declaration
233 *
234 * @param a
235 */
236 void setRows(Result a) {
237
238 rRoot = a.rRoot;
239 rTail = a.rTail;
240 iSize = a.iSize;
241 }
242
243 /**
244 * Method declaration
245 *
246 * @param d
247 */
248 void add(Object d[]) {
249
250 Record r = new Record();
251
252 r.data = d;
253
254 if (rRoot == null) {
255 rRoot = r;
256 } else {
257 rTail.next = r;
258 }
259
260 rTail = r;
261
262 iSize++;
263 }
264
265 /**
266 * Method declaration
267 *
268 * @param limitstart number of records to discard at the head
269 * @param limitcount number of records to keep, all the rest if 0
270 */
271
272 // fredt@users 20020130 - patch 1.7.0 by fredt
273 // rewritten and moved from Select.java
274 void trimResult(int limitstart, int limitcount) {
275
276 Record n = rRoot;
277
278 if (n == null) {
279 return;
280 }
281
282 if (limitstart >= iSize) {
283 iSize = 0;
284 rRoot = rTail = null;
285
286 return;
287 }
288
289 iSize -= limitstart;
290
291 for (int i = 0; i < limitstart; i++) {
292 n = n.next;
293
294 if (n == null) {
295
296 // if iSize is consistent this block will never be reached
297 iSize = 0;
298 rRoot = rTail = n;
299
300 return;
301 }
302 }
303
304 rRoot = n;
305
306 if (limitcount == 0 || limitcount >= iSize) {
307 return;
308 }
309
310 for (int i = 1; i < limitcount; i++) {
311 n = n.next;
312
313 if (n == null) {
314
315 // if iSize is consistent this block will never be reached
316 return;
317 }
318 }
319
320 n.next = null;
321 rTail = n;
322 }
323
324 /**
325 * Method declaration
326 *
327 * @throws SQLException
328 */
329
330 // fredt@users 20020130 - patch 1.7.0 by fredt
331 // to ensure consistency of r.rTail r.iSize in all set operations
332 void removeDuplicates() throws SQLException {
333
334 if (rRoot == null) {
335 return;
336 }
337
338 int len = getColumnCount();
339 int order[] = new int[len];
340 int way[] = new int[len];
341
342 for (int i = 0; i < len; i++) {
343 order[i] = i;
344 way[i] = 1;
345 }
346
347 sortResult(order, way);
348
349 Record n = rRoot;
350
351 for (;;) {
352 Record next = n.next;
353
354 if (next == null) {
355 break;
356 }
357
358 if (compareRecord(n.data, next.data, len) == 0) {
359 n.next = next.next;
360
361 iSize--;
362 } else {
363 n = next;
364 }
365 }
366
367 rTail = n;
368
369 Trace.doAssert(rTail.next == null,
370 "rTail not correct in Result.removeDuplicates iSise ="
371 + iSize);
372 }
373
374 /**
375 * Method declaration
376 *
377 * @param minus
378 * @throws SQLException
379 */
380 void removeSecond(Result minus) throws SQLException {
381
382 removeDuplicates();
383 minus.removeDuplicates();
384
385 int len = getColumnCount();
386 Record n = rRoot;
387 Record last = rRoot;
388 boolean rootr = true; // checking rootrecord
389 Record n2 = minus.rRoot;
390 int i = 0;
391
392 while (n != null && n2 != null) {
393 i = compareRecord(n.data, n2.data, len);
394
395 if (i == 0) {
396 if (rootr) {
397 rRoot = last = n.next;
398 } else {
399 last.next = n.next;
400 }
401
402 n = n.next;
403
404 iSize--;
405 } else if (i > 0) { // r > minus
406 n2 = n2.next;
407 } else { // r < minus
408 last = n;
409 rootr = false;
410 n = n.next;
411 }
412 }
413
414 for (; n != null; ) {
415 last = n;
416 n = n.next;
417 }
418
419 rTail = last;
420
421 Trace.doAssert(
422 (rRoot == null && rTail == null) || rTail.next == null,
423 "rTail not correct in Result.removeSecond iSise =" + iSize);
424 }
425
426 /**
427 * Method declaration
428 *
429 * @param r2
430 * @throws SQLException
431 */
432 void removeDifferent(Result r2) throws SQLException {
433
434 removeDuplicates();
435 r2.removeDuplicates();
436
437 int len = getColumnCount();
438 Record n = rRoot;
439 Record last = rRoot;
440 boolean rootr = true; // checking rootrecord
441 Record n2 = r2.rRoot;
442 int i = 0;
443
444 iSize = 0;
445
446 while (n != null && n2 != null) {
447 i = compareRecord(n.data, n2.data, len);
448
449 if (i == 0) { // same rows
450 if (rootr) {
451 rRoot = n; // make this the first record
452 } else {
453 last.next = n; // this is next record in resultset
454 }
455
456 rootr = false;
457 last = n; // this is last record in resultset
458 n = n.next;
459 n2 = n2.next;
460
461 iSize++;
462 } else if (i > 0) { // r > r2
463 n2 = n2.next;
464 } else { // r < r2
465 n = n.next;
466 }
467 }
468
469 if (rootr) { // if no lines in resultset
470 rRoot = null; // then return null
471 last = null;
472 } else {
473 last.next = null; // else end resultset
474 }
475
476 rTail = last;
477
478 Trace.doAssert(
479 (rRoot == null && rTail == null) || rTail.next == null,
480 "rTail not correct in Result.removeDifference iSise =" + iSize);
481 }
482
483 /**
484 * Method declaration
485 *
486 * @param order
487 * @param way
488 * @throws SQLException
489 */
490 void sortResult(int order[], int way[]) throws SQLException {
491
492 if (rRoot == null || rRoot.next == null) {
493 return;
494 }
495
496 Record source0, source1;
497 Record target[] = new Record[2];
498 Record targetlast[] = new Record[2];
499 int dest = 0;
500 Record n = rRoot;
501
502 while (n != null) {
503 Record next = n.next;
504
505 n.next = target[dest];
506 target[dest] = n;
507 n = next;
508 dest ^= 1;
509 }
510
511 for (int blocksize = 1; target[1] != null; blocksize <<= 1) {
512 source0 = target[0];
513 source1 = target[1];
514 target[0] = target[1] = targetlast[0] = targetlast[1] = null;
515
516 for (dest = 0; source0 != null; dest ^= 1) {
517 int n0 = blocksize,
518 n1 = blocksize;
519
520 while (true) {
521 if (n0 == 0 || source0 == null) {
522 if (n1 == 0 || source1 == null) {
523 break;
524 }
525
526 n = source1;
527 source1 = source1.next;
528
529 n1--;
530 } else if (n1 == 0 || source1 == null) {
531 n = source0;
532 source0 = source0.next;
533
534 n0--;
535 } else if (compareRecord(
536 source0.data, source1.data, order, way) > 0) {
537 n = source1;
538 source1 = source1.next;
539
540 n1--;
541 } else {
542 n = source0;
543 source0 = source0.next;
544
545 n0--;
546 }
547
548 if (target[dest] == null) {
549 target[dest] = n;
550 } else {
551 targetlast[dest].next = n;
552 }
553
554 targetlast[dest] = n;
555 n.next = null;
556 }
557 }
558 }
559
560 rRoot = target[0];
561 rTail = targetlast[0];
562
563 Trace.doAssert(rTail.next == null,
564 "rTail not correct in Result.sortResult iSise ="
565 + iSize);
566 }
567
568 /**
569 * Method declaration
570 *
571 * @param a
572 * @param b
573 * @param order
574 * @param way
575 * @return
576 * @throws SQLException
577 */
578 private int compareRecord(Object a[], Object b[], int order[],
579 int way[]) throws SQLException {
580
581 int i = Column.compare(a[order[0]], b[order[0]], colType[order[0]]);
582
583 if (i == 0) {
584 for (int j = 1; j < order.length; j++) {
585 i = Column.compare(a[order[j]], b[order[j]],
586 colType[order[j]]);
587
588 if (i != 0) {
589 return i * way[j];
590 }
591 }
592 }
593
594 return i * way[0];
595 }
596
597 /**
598 * Method declaration
599 *
600 * @param a
601 * @param b
602 * @param len
603 * @return
604 * @throws SQLException
605 */
606 private int compareRecord(Object a[], Object b[],
607 int len) throws SQLException {
608
609 for (int j = 0; j < len; j++) {
610 int i = Column.compare(a[j], b[j], colType[j]);
611
612 if (i != 0) {
613 return i;
614 }
615 }
616
617 return 0;
618 }
619
620 // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
621
622 /**
623 * Method declaration
624 *
625 * @return
626 * @throws SQLException
627 */
628 byte[] getBytes() throws SQLException {
629
630 try {
631 DatabaseRowOutputInterface out = new BinaryServerRowOutput();
632
633 out.writeIntData(iMode);
634
635 if (iMode == UPDATECOUNT) {
636 out.writeIntData(iUpdateCount);
637 } else if (iMode == ERROR) {
638 out.writeIntData(errorCode);
639 out.writeString(sError);
640 } else {
641 int l = iColumnCount;
642
643 out.writeIntData(l);
644
645 Record n = rRoot;
646
647 for (int i = 0; i < l; i++) {
648 out.writeType(colType[i]);
649 out.writeString(sLabel[i]);
650 out.writeString(sTable[i]);
651 out.writeString(sName[i]);
652 }
653
654 while (n != null) {
655 out.writeData(l, colType, n.data);
656
657 n = n.next;
658 }
659 }
660
661 return out.toByteArray();
662 } catch (IOException e) {
663 throw Trace.error(Trace.TRANSFER_CORRUPTED);
664 }
665 }
666
667 /**
668 * Method declaration
669 *
670 * @param columns
671 */
672 private void prepareData(int columns) {
673
674 iMode = DATA;
675 sLabel = new String[columns];
676 sTable = new String[columns];
677 sName = new String[columns];
678 isLabelQuoted = new boolean[columns];
679 colType = new int[columns];
680 colSize = new int[columns];
681 colScale = new int[columns];
682 }
683 }