Quadcap Embeddable Database

com/quadcap/sql/Key.java

Go to the documentation of this file.
00001 package com.quadcap.sql; 00002 00003 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.ByteArrayOutputStream; 00042 import java.io.IOException; 00043 import java.io.OutputStream; 00044 00045 import java.sql.SQLException; 00046 00047 import com.quadcap.sql.io.ObjectOutputStream; 00048 00049 import com.quadcap.sql.file.ByteUtil; 00050 import com.quadcap.sql.file.SubPageManager; 00051 00052 import com.quadcap.sql.index.Comparator; 00053 00054 import com.quadcap.sql.types.KeyStream; 00055 import com.quadcap.sql.types.Type; 00056 import com.quadcap.sql.types.Value; 00057 import com.quadcap.sql.types.ValueInteger; 00058 00059 import com.quadcap.util.Debug; 00060 import com.quadcap.util.Util; 00061 00062 /** 00063 * Micro-optimized (-;) key serialization and comparison. This class 00064 * is designed to eliminate copies and allocations during key comparison 00065 * and serialization operations. 00066 * 00067 * @author Stan Bailes 00068 */ 00069 public class Key extends Comparator { 00070 //#ifdef DEBUG 00071 static final boolean paranoid = false; 00072 //#endif 00073 00074 boolean[] asc; 00075 00076 public Key(int cnt) { 00077 asc = new boolean[cnt]; 00078 for (int i = 0; i < cnt; i++) asc[i] = true; 00079 } 00080 00081 public Key(boolean[] asc) { 00082 this.asc = asc; 00083 } 00084 00085 public static byte[] makeKey(Tuple tuple, Row row, int[] map, long rowNum, 00086 boolean doRowNum) 00087 throws SQLException 00088 { 00089 //#ifdef DEBUG 00090 if (false) { 00091 StringBuffer sb = new StringBuffer("makeKey("); 00092 sb.append(row.toString()); 00093 sb.append(" (" + ((Object)row).toString()); 00094 sb.append(", ["); 00095 for (int i = 0; i < map.length; i++) { 00096 if (i>0) sb.append(','); 00097 sb.append(map[i]); 00098 } 00099 sb.append("], "); 00100 sb.append(SubPageManager.toString(rowNum)); 00101 sb.append(", "); 00102 sb.append(doRowNum); 00103 sb.append(")"); 00104 sb.append("\nFROM:\n"); 00105 sb.append(Util.stackTrace()); 00106 Debug.println(sb.toString()); 00107 } 00108 //#endif 00109 try { 00110 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 00111 KeyStream out = new KeyStream(bos, Database.isCaseSensitive); 00112 //#ifdef DEBUG 00113 int cnt = 0; 00114 //#endif 00115 if (map != null) { 00116 //#ifdef DEBUG 00117 cnt = map.length; 00118 //#endif 00119 for (int i = 0; i < map.length; i++) { 00120 int col = map[i]; 00121 Value v = row.item(col); 00122 if (tuple != null) { 00123 Column c = tuple.getColumn(col); 00124 Type t = c.getType(); 00125 v = c.getType().convert(v); 00126 } 00127 v.serializeKey(out); 00128 } 00129 } else { 00130 //#ifdef DEBUG 00131 cnt = row.size(); 00132 //#endif 00133 for (int i = 1; i <= row.size(); i++) { 00134 Value v = row.item(i); 00135 v.serializeKey(out); 00136 } 00137 } 00138 if (doRowNum) { 00139 out.writeLong(rowNum); 00140 //#ifdef DEBUG 00141 cnt++; 00142 //#endif 00143 } 00144 byte[] ret = bos.toByteArray(); 00145 //#ifdef DEBUG 00146 if (paranoid) { 00147 Key k = new Key(cnt); 00148 k.checkKey(ret, 0, ret.length); 00149 } 00150 //#endif 00151 return ret; 00152 } catch (IOException e) { 00153 throw DbException.wrapThrowable(e); 00154 } 00155 } 00156 00157 public static byte[] makeKey(Row row) throws SQLException { 00158 return makeKey(null, row, null, 0, false); 00159 } 00160 00161 /** 00162 * Null comparison for GROUP BY 00163 */ 00164 public boolean compareNulls(byte[] a, byte[] b) { 00165 // XXX hack 00166 return compare(a, b) == 0; 00167 } 00168 00169 public static final int COMPOUND = KeyStream.COMPOUND; 00170 public static final int INTEGER = KeyStream.INTEGER; 00171 public static final int STRING = KeyStream.STRING; 00172 public static final int NULL = KeyStream.NULL; 00173 00174 public int compare(byte[] a, byte[] b) { 00175 return compare(a, 0, a.length, b, 0, b.length); 00176 } 00177 00178 //#ifdef DEBUG 00179 public final int compare(byte[] a, int aoff, int alen, 00180 byte[] b, int boff, int blen) { 00181 try { 00182 if (paranoid) { 00183 checkKey(a, aoff, alen); 00184 checkKey(b, boff, blen); 00185 } 00186 int ret = compare2(a, aoff, alen, b, boff, blen); 00187 if (Trace.bit(6)) { 00188 Debug.println("Compare " + asc.length + " fields, ret=" + ret + ": " + 00189 toString(a, aoff, alen) + 00190 "(" + Util.hexBytes(a, aoff, alen) + ") " + 00191 " vs " + 00192 toString(b, boff, blen) + 00193 "(" + Util.hexBytes(b, boff, blen) + ") "); 00194 //Debug.println(Util.stackTrace()); 00195 } 00196 return ret; 00197 } catch (RuntimeException t) { 00198 Debug.print(t); 00199 Debug.println("asc.len = " + asc.length); 00200 Debug.println("a = " + toString(a, aoff, alen)); 00201 Debug.println("a = " + Util.hexBytes(a, aoff, alen)); 00202 Debug.println("b = " + toString(b, boff, blen)); 00203 Debug.println("b = " + Util.hexBytes(b, boff, blen)); 00204 throw t; 00205 } 00206 } 00207 00208 public void checkKey(byte[] buf, int off, int len) { 00209 if (getKeyLen(buf, off) != len) { 00210 Debug.println(Util.strBytes(buf, off, len)); 00211 Debug.println(toString(buf, off, len)); 00212 throw new RuntimeException("bad key len: " + 00213 getKeyLen(buf, off) + " vs " + 00214 len); 00215 } 00216 } 00217 00218 final int compare2 00219 //#else 00220 //- public final int compare 00221 //#endif 00222 (byte[] a, int aoff, int alen, 00223 byte[] b, int boff, int blen) 00224 { 00225 int i = 0; 00226 int askip = 0; 00227 int bskip = 0; 00228 int scalar = 0; 00229 while (i < asc.length) { 00230 int ac = a[aoff++] & 0xff; 00231 int bc = b[boff++] & 0xff; 00232 final int atype = ac & 3; 00233 final int btype = bc & 3; 00234 int al = (ac >> 2) & 0x1f; 00235 int bl = (bc >> 2) & 0x1f; 00236 if ((ac & 0x80) != 0) { 00237 al <<= 8; 00238 al |= (a[aoff++] & 0xff); 00239 } 00240 if ((bc & 0x80) != 0) { 00241 bl <<= 8; 00242 bl |= (b[boff++] & 0xff); 00243 } 00244 //Debug.println("Field " + i + ": " + atype + " - " + btype); 00245 //Debug.println("ac/al = " + ac + "/" + al + ", bc/bl = " + bc + "/" + bl); 00246 switch ((atype << 2) | btype) { 00247 case (COMPOUND << 2) | COMPOUND: 00248 askip = al; 00249 bskip = bl; 00250 break; 00251 case (INTEGER << 2) | INTEGER: 00252 int signMask = 0xffffffff; 00253 if (al > bl) { 00254 int ab = a[aoff++]; 00255 if (ab != 0) return (ab < 0 ? -1 : 1) * (asc[i] ? 1 : -1); 00256 while (--al > bl) { 00257 if (a[aoff++] != 0) { 00258 return asc[i] ? 1 : -1; 00259 } 00260 } 00261 signMask = 0xff; 00262 } 00263 00264 if (bl > al) { 00265 int bb = b[boff++]; 00266 if (bb != 0) return (bb < 0 ? 1 : -1) * (asc[i] ? 1 : -1); 00267 while (--bl > al) { 00268 if (b[boff++] != 0) { 00269 return asc[i] ? -1 : 1; 00270 } 00271 } 00272 signMask = 0xff; 00273 } 00274 00275 int neg = (a[aoff] < 0) ? -1 : 1; 00276 int rneg = 1; 00277 while (al-- > 0) { 00278 int ab = a[aoff++] & signMask; 00279 int bb = b[boff++] & signMask; 00280 if (ab != bb) { 00281 int xa = a[aoff-1]; 00282 return (ab < bb ? 1 : -1) * (asc[i] ? -1 : 1) * rneg; 00283 } 00284 rneg = neg; 00285 signMask = 0xff; 00286 } 00287 break; 00288 case (STRING << 2) | STRING: 00289 while (al > 0 && bl > 0) { 00290 int ab = a[aoff++] & 0xff; 00291 int bb = b[boff++] & 0xff; 00292 if (ab != bb) { 00293 return (ab < bb ? 1 : -1) * (asc[i] ? -1 : 1); 00294 } 00295 al--; 00296 bl--; 00297 } 00298 while (al-- > 0) { 00299 if (a[aoff++] != 0) return asc[i] ? 1 : -1; 00300 } 00301 while (bl-- > 0) { 00302 if (b[boff++] != 0) return asc[i] ? -1 : 1; 00303 } 00304 break; 00305 case (NULL << 2) | NULL: 00306 // length == 0: first null, otherwise last null 00307 if (al == 0) { 00308 if (bl != 0) return -1; 00309 } else { 00310 if (bl == 0) return 1; 00311 } 00312 break; 00313 case (NULL << 2) | COMPOUND: 00314 case (NULL << 2) | INTEGER: 00315 case (NULL << 2) | STRING: 00316 return al == 0 ? -1 : 1; 00317 case (COMPOUND << 2) | NULL: 00318 case (INTEGER << 2) | NULL: 00319 case (STRING << 2) | NULL: 00320 return bl == 0 ? 1 : -1; 00321 00322 case (COMPOUND << 2) | INTEGER: 00323 askip = al; 00324 bskip = 1; 00325 boff--; 00326 break; 00327 case (INTEGER << 2) | COMPOUND: 00328 bskip = bl; 00329 askip = 1; 00330 aoff--; 00331 break; 00332 00333 case (STRING << 2) | INTEGER: 00334 return 1; 00335 case (INTEGER << 2) | STRING: 00336 return -1; 00337 00338 default: 00339 throw new RuntimeException("Cardinality error"); 00340 } 00341 askip--; 00342 bskip--; 00343 if (askip < 0) { 00344 if (bskip < 0) { 00345 // end of both compound fields 00346 i++; 00347 } else { 00348 // end of a, more fields left in b 00349 while (bskip-- >= 0) { 00350 bc = b[boff++] & 0xff; 00351 bl = (bc >> 2) & 0x1f; 00352 if ((bc & 0x80) != 0) { 00353 bc <<= 8; 00354 bc |= (b[boff++] & 0xff); 00355 } 00356 for (int j = 0; j < bl; j++) { 00357 if (b[boff++] != 0) return -1; 00358 } 00359 i++; 00360 } 00361 } 00362 } else { 00363 if (bskip < 0) { 00364 // end of b, more fields left in a 00365 while (askip-- >= 0) { 00366 ac = a[aoff++] & 0xff; 00367 al = (ac >> 2) & 0x1f; 00368 if ((ac & 0x80) != 0) { 00369 ac <<= 8; 00370 ac |= (a[aoff++] & 0xff); 00371 } 00372 for (int j = 0; j < al; j++) { 00373 if (a[aoff++] != 0) return 1; 00374 } 00375 i++; 00376 } 00377 } else { 00378 // next compound field 00379 } 00380 } 00381 } 00382 return 0; 00383 } 00384 00385 public int getKeyLen(byte[] a, int aoff) { 00386 int start = aoff; 00387 int i = 0; 00388 int skip = 0; 00389 while (i < asc.length) { 00390 int ac = a[aoff++] & 0xff; 00391 final int atype = ac & 3; 00392 int al = (ac >> 2) & 0x1f; 00393 if ((ac & 0x80) != 0) { 00394 al <<= 8; 00395 al |= (a[aoff++] & 0xff); 00396 } 00397 switch (atype) { 00398 case NULL: 00399 break; 00400 case INTEGER: 00401 case STRING: 00402 aoff += al; 00403 break; 00404 case COMPOUND: 00405 skip = al; 00406 break; 00407 } 00408 skip--; 00409 if (skip < 0) i++; 00410 } 00411 return aoff - start; 00412 } 00413 00414 //#ifdef DEBUG 00415 public static String toStringHelper(byte[] key) { 00416 return toStringHelper(key, 0, key.length); 00417 } 00418 00419 public String toString(byte[] key, int off, int xlen) { 00420 return toStringHelper(key, off, xlen); 00421 } 00422 00423 public static String toStringHelper(byte[] key, int off, int xlen) { 00424 StringBuffer sb = new StringBuffer(); 00425 int lim = off + xlen; 00426 String delim = ""; 00427 String sdelim = ""; 00428 int clen = -1; 00429 while (off < lim) { 00430 clen--; 00431 sb.append(delim); 00432 sb.append(sdelim); 00433 if (clen > 0) sdelim =","; 00434 delim = ", "; 00435 int c = key[off++] & 0xff; 00436 int type = c & 3; 00437 int len = (c >> 2) & 0x1f; 00438 if ((c & 0x80) != 0) { 00439 len <<= 8; 00440 len |= key[off++]; 00441 } 00442 //Debug.println("TYPE: " + type + ", off = " + off + 00443 // ", len = " + len); 00444 switch (type) { 00445 case COMPOUND: 00446 sb.append("{"); 00447 sdelim = ""; 00448 clen = len; 00449 break; 00450 case INTEGER: 00451 sb.append("I("); 00452 sb.append(Util.hexBytes(key, off, len)); 00453 sb.append(")"); 00454 off += len; 00455 break; 00456 case STRING: 00457 sb.append("S("); 00458 //sb.append(Util.hexBytes(key, off, len)); 00459 try { 00460 sb.append(new String(key, off, len)); 00461 } catch (Throwable t) { 00462 sb.append("key[" + off + ":" + len + "]:*****(" + new String(key) + ")"); 00463 } 00464 sb.append(")"); 00465 off += len; 00466 break; 00467 case NULL: 00468 sb.append("NULL"); 00469 break; 00470 } 00471 if (clen == 0) { 00472 sb.append("}"); 00473 sdelim = ""; 00474 } 00475 } 00476 return sb.toString(); 00477 } 00478 00479 public static void test1() throws IOException { 00480 com.quadcap.sql.file.BlockFile f = 00481 new com.quadcap.sql.file.BlockFile("test1.bf", "rw", 00482 new java.util.Properties(), 00483 4096, 256); 00484 long root = f.newPage(); 00485 Key key = new Key(1); 00486 com.quadcap.sql.index.Btree tree = new com.quadcap.sql.index.Btree(f, key, root, true); 00487 byte[] vk = new byte[5]; 00488 for (int i = 0; i < 40000; i++) { 00489 byte[] r = makeRandomKey(); 00490 ByteUtil.putInt(vk, 0, i); 00491 tree.set(r, vk); 00492 if ((i % 1000) == 0) System.out.print("."); 00493 } 00494 } 00495 00496 static java.util.Random random = new java.util.Random(666); 00497 00498 public static byte[] makeRandomKey() throws IOException { 00499 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 00500 KeyStream k = new KeyStream(bos, true); 00501 StringBuffer sb = new StringBuffer(); 00502 int lim = 40 + random.nextInt(50); 00503 for (int i = 0; i < lim; i++) { 00504 sb.append((char)random.nextInt(255)); 00505 } 00506 k.writeChars(sb.toString()); 00507 return bos.toByteArray(); 00508 } 00509 00510 public static void main(String[] args) { 00511 try { 00512 test1(); 00513 } catch (Exception e) { 00514 Debug.print(e); 00515 } 00516 } 00517 //#endif 00518 }