| 1 | /* ============================================================= |
| 2 | * SmallSQL : a free Java DBMS library for the Java(tm) platform |
| 3 | * ============================================================= |
| 4 | * |
| 5 | * (C) Copyright 2004-2006, by Volker Berlin. |
| 6 | * |
| 7 | * Project Info: http://www.smallsql.de/ |
| 8 | * |
| 9 | * This library is free software; you can redistribute it and/or modify it |
| 10 | * under the terms of the GNU Lesser General Public License as published by |
| 11 | * the Free Software Foundation; either version 2.1 of the License, or |
| 12 | * (at your option) any later version. |
| 13 | * |
| 14 | * This library is distributed in the hope that it will be useful, but |
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
| 17 | * License for more details. |
| 18 | * |
| 19 | * You should have received a copy of the GNU Lesser General Public |
| 20 | * License along with this library; if not, write to the Free Software |
| 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
| 22 | * USA. |
| 23 | * |
| 24 | * [Java is a trademark or registered trademark of Sun Microsystems, Inc. |
| 25 | * in the United States and other countries.] |
| 26 | * |
| 27 | * --------------- |
| 28 | * StoreImpl.java |
| 29 | * --------------- |
| 30 | * Author: Volker Berlin |
| 31 | * |
| 32 | */ |
| 33 | package smallsql.database; |
| 34 | |
| 35 | import java.io.*; |
| 36 | import java.sql.SQLException; |
| 37 | |
| 38 | |
| 39 | public class StoreImpl extends Store { |
| 40 | |
| 41 | private static final int DEFAULT_PAGE_SIZE = 8192; // 8 Kb |
| 42 | private static final int PAGE_MAGIC = 0x12DD13DE; // are used for repairing a table |
| 43 | /** |
| 44 | * The structur of the Page Control Block is: |
| 45 | * 4 byte - page magic |
| 46 | * 4 Byte - Status 0:normal; 1:deleted; 2:Pointer to an update; 3: updated page |
| 47 | * 4 Byte - used size of the page |
| 48 | * 4 byte - physical size of the page |
| 49 | * 4 byte - offset to the next page |
| 50 | * 8 byte - position of an updated page |
| 51 | */ |
| 52 | private static final int PAGE_CONTROL_SIZE = 28; |
| 53 | private static final byte[] page_control = new byte[PAGE_CONTROL_SIZE]; |
| 54 | private int status; // valid value are follow: |
| 55 | private static final int NORMAL = 0; |
| 56 | private static final int DELETED = 1; |
| 57 | /** |
| 58 | * Using of UPDATE_POINTER and UPDATED_PAGE |
| 59 | * If a page are updated and the new data are larger as the old data then |
| 60 | * the old page are changed to a UPDATE_POINTER. The new page is |
| 61 | * a UPDATED_PAGE. On reading the pages only the UPDATE_POINTER is read. |
| 62 | * The UPDATED_PAGE are skipped. Thats a row change not it position. |
| 63 | */ |
| 64 | private static final int UPDATE_POINTER = 2; |
| 65 | private static final int UPDATED_PAGE = 3; |
| 66 | |
| 67 | final private Table table; |
| 68 | private byte[] page; // Daten einer Page |
| 69 | private StorePage storePage; |
| 70 | private long filePos; // Position in der Datei |
| 71 | private int sizeUsed; |
| 72 | private int sizePhysical; |
| 73 | private int nextPageOffset; |
| 74 | private long filePosUpdated; |
| 75 | private int type; |
| 76 | |
| 77 | private StoreImpl updatePointer; |
| 78 | |
| 79 | private StoreImpl( Table table, StorePage storePage, int type, long filePos ){ |
| 80 | this.table = table; |
| 81 | this.storePage = storePage; |
| 82 | this.filePos = filePos; |
| 83 | this.type = type; |
| 84 | } |
| 85 | |
| 86 | |
| 87 | /** folgende Arten von StoreImpl Types sind möglich |
| 88 | INSERT: Eine Page die neue Daten enthalten wird. filePos ist noch nicht spezifiziert. |
| 89 | CREATE: Eine spezielle Art von INSERT. |
| 90 | SELECT: Nur Leseoperationen sind möglich. |
| 91 | UPDATE: Hat eine filePos, wenn neue größe zu klein, muß alte Page gelöscht werden, und neue hinzugefügt werden. |
| 92 | DELETE: hat keinen Cache sondern nur die filePos, um das flag zu schreiben. |
| 93 | */ |
| 94 | static StoreImpl createStore( Table table, StorePage storePage, int type, long filePos ) throws Exception{ |
| 95 | StoreImpl store = new StoreImpl(table, storePage, type, filePos); |
| 96 | RandomAccessFile raFile = storePage.raFile; |
| 97 | switch(type){ |
| 98 | case SQLTokenizer.LONGVARBINARY: |
| 99 | // wird verwendet zum speichern von LONGVARBINARY und LONGVARCHAR |
| 100 | store.page = new byte[(int)filePos + PAGE_CONTROL_SIZE]; |
| 101 | store.filePos = -1; |
| 102 | break; |
| 103 | case SQLTokenizer.INSERT: |
| 104 | case SQLTokenizer.CREATE: |
| 105 | store.page = new byte[DEFAULT_PAGE_SIZE]; |
| 106 | break; |
| 107 | case SQLTokenizer.SELECT: |
| 108 | case SQLTokenizer.UPDATE: |
| 109 | case SQLTokenizer.DELETE: |
| 110 | if(storePage.page == null){ |
| 111 | if(filePos >= raFile.length()-PAGE_CONTROL_SIZE) return null; |
| 112 | raFile.seek(filePos); |
| 113 | synchronized(page_control){ |
| 114 | raFile.read(page_control); |
| 115 | store.page = page_control; |
| 116 | store.readPageHeader(); |
| 117 | } |
| 118 | store.page = new byte[store.sizeUsed]; |
| 119 | raFile.seek(filePos); |
| 120 | raFile.read(store.page); |
| 121 | }else{ |
| 122 | store.page = storePage.page; |
| 123 | store.readPageHeader(); |
| 124 | } |
| 125 | store = store.loadUpdatedStore(); |
| 126 | break; |
| 127 | default: throw new Error(); |
| 128 | } |
| 129 | store.offset = PAGE_CONTROL_SIZE; |
| 130 | return store; |
| 131 | } |
| 132 | |
| 133 | |
| 134 | /** |
| 135 | * Recreate a StoreImpl from an uncommited StorePage. |
| 136 | */ |
| 137 | static StoreImpl recreateStore( Table table, StorePage storePage, int type) throws Exception{ |
| 138 | StoreImpl store = new StoreImpl(table, storePage, type, -1); |
| 139 | store.page = storePage.page; |
| 140 | store.readPageHeader(); |
| 141 | store = store.loadUpdatedStore(); |
| 142 | store.offset = PAGE_CONTROL_SIZE; |
| 143 | return store; |
| 144 | } |
| 145 | |
| 146 | |
| 147 | private final void readPageHeader() throws SQLException{ |
| 148 | if(readInt() != PAGE_MAGIC) |
| 149 | throw Utils.createSQLException("Corrupt table page at position:"+filePos); |
| 150 | status = readInt(); |
| 151 | sizeUsed = readInt(); |
| 152 | sizePhysical = readInt(); |
| 153 | nextPageOffset = readInt(); |
| 154 | filePosUpdated = readLong(); |
| 155 | } |
| 156 | |
| 157 | |
| 158 | final private StoreImpl loadUpdatedStore() throws Exception{ |
| 159 | if(status != UPDATE_POINTER) return this; |
| 160 | StoreImpl storeTemp = table.getStore( ((TableStorePage)storePage).con, filePosUpdated, type); |
| 161 | storeTemp.updatePointer = this; |
| 162 | return storeTemp; |
| 163 | } |
| 164 | |
| 165 | |
| 166 | private void resizePage(int minNewSize){ |
| 167 | int newSize = Math.max(minNewSize, page.length*2); |
| 168 | byte[] newPage = new byte[newSize]; |
| 169 | System.arraycopy( page, 0, newPage, 0, page.length); |
| 170 | page = newPage; |
| 171 | } |
| 172 | |
| 173 | |
| 174 | boolean isValidPage(){ |
| 175 | return status == NORMAL || (status == UPDATED_PAGE && updatePointer != null); |
| 176 | } |
| 177 | |
| 178 | int getUsedSize(){ |
| 179 | return sizeUsed; |
| 180 | } |
| 181 | |
| 182 | long getNextPagePos(){ |
| 183 | if(updatePointer != null) return updatePointer.getNextPagePos(); |
| 184 | if(nextPageOffset <= 0){ |
| 185 | nextPageOffset = sizePhysical; |
| 186 | } |
| 187 | return filePos + nextPageOffset; |
| 188 | } |
| 189 | |
| 190 | |
| 191 | /** |
| 192 | * |
| 193 | * @param con Is needed to add this page to the commitPages. If it null then it save directly without rollback option. |
| 194 | * @return The file position if con == null. |
| 195 | * @throws SQLException |
| 196 | */ |
| 197 | long writeFinsh(SSConnection con) throws SQLException{ |
| 198 | switch(type){ |
| 199 | case SQLTokenizer.LONGVARBINARY: |
| 200 | case SQLTokenizer.INSERT: |
| 201 | case SQLTokenizer.CREATE: |
| 202 | sizeUsed = sizePhysical = offset; |
| 203 | break; |
| 204 | case SQLTokenizer.UPDATE: |
| 205 | if(status != UPDATE_POINTER) { |
| 206 | sizeUsed = offset; |
| 207 | break; |
| 208 | } |
| 209 | case SQLTokenizer.DELETE: |
| 210 | sizeUsed = PAGE_CONTROL_SIZE; |
| 211 | break; |
| 212 | //SQLTokenizer.SELECT should not occur here |
| 213 | default: throw new Error(""+type); |
| 214 | } |
| 215 | offset = 0; |
| 216 | writeInt( PAGE_MAGIC ); // for repair |
| 217 | writeInt( status); |
| 218 | writeInt( sizeUsed ); |
| 219 | writeInt( sizePhysical ); |
| 220 | writeInt( 0 ); //nextPageOffset |
| 221 | writeLong( filePosUpdated ); // Pointer of an updated page |
| 222 | storePage.setPageData( page, sizeUsed ); //TODO page sollte eigentlich beim einlesen gesetzt sein |
| 223 | if(con == null){ |
| 224 | // the pointer is needed to safe in another page |
| 225 | // this produce not referenced pages on rollback |
| 226 | return storePage.commit(); |
| 227 | }else{ |
| 228 | return 0; |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | |
| 233 | final private void createWriteLock() throws SQLException{ |
| 234 | TableStorePage storePageWrite = table.requestWriteLock( ((TableStorePage)storePage).con, (TableStorePage)storePage ); |
| 235 | if(storePageWrite == null) |
| 236 | throw Utils.createSQLException("Row is locked from another Connection"); |
| 237 | storePage = storePageWrite; |
| 238 | } |
| 239 | |
| 240 | /** |
| 241 | * Is call from updateRow(). |
| 242 | * The offset of newData must be at the end of the data. It is used as new page size. |
| 243 | */ |
| 244 | void updateFinsh(SSConnection con, StoreImpl newData) throws SQLException{ |
| 245 | type = SQLTokenizer.UPDATE; |
| 246 | createWriteLock(); |
| 247 | if(newData.offset <= sizePhysical || filePos == -1){ |
| 248 | // the old page can be overwrite because it it large enough |
| 249 | page = newData.page; //newData is only a temp StoreImpl |
| 250 | offset = newData.offset; |
| 251 | if(sizePhysical < offset) sizePhysical = offset; // occur only on updates of not commited inserts (filePos == -1 |
| 252 | writeFinsh(con); |
| 253 | }else{ |
| 254 | // we need to create a new page because the old page is to small |
| 255 | newData.status = UPDATED_PAGE; |
| 256 | if(updatePointer == null){ |
| 257 | // we need to create a new page and change the old page to a UPDATE_POINTER |
| 258 | ((TableStorePage)newData.storePage).lockType = Table.LOCK_INSERT; |
| 259 | filePosUpdated = newData.writeFinsh(null); |
| 260 | status = UPDATE_POINTER; |
| 261 | }else{ |
| 262 | // we need to create a new page and delete the old page |
| 263 | ((TableStorePage)newData.storePage).lockType = Table.LOCK_INSERT; |
| 264 | updatePointer.filePosUpdated = newData.writeFinsh(null); |
| 265 | updatePointer.status = UPDATE_POINTER; |
| 266 | updatePointer.type = SQLTokenizer.UPDATE; |
| 267 | updatePointer.createWriteLock(); |
| 268 | updatePointer.writeFinsh(con); |
| 269 | status = DELETED; |
| 270 | } |
| 271 | writeFinsh(con); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | /*============================================================================== |
| 276 | |
| 277 | Write und Read Methoden |
| 278 | |
| 279 | ==============================================================================*/ |
| 280 | private int offset; // aktuelle read/write Position in der Page |
| 281 | |
| 282 | |
| 283 | int getCurrentOffsetInPage(){ |
| 284 | return offset; |
| 285 | } |
| 286 | |
| 287 | |
| 288 | void setCurrentOffsetInPage(int newOffset){ |
| 289 | this.offset = newOffset; |
| 290 | } |
| 291 | |
| 292 | |
| 293 | void writeByte( int value ){ |
| 294 | int newSize = offset + 1; |
| 295 | if(newSize >= page.length) resizePage(newSize); |
| 296 | |
| 297 | page[ offset++ ] = (byte)(value); |
| 298 | } |
| 299 | |
| 300 | int readByte(){ |
| 301 | return page[ offset++ ]; |
| 302 | } |
| 303 | |
| 304 | int readUnsignedByte(){ |
| 305 | return page[ offset++ ] & 0xFF; |
| 306 | } |
| 307 | |
| 308 | void writeBoolean( boolean value ){ |
| 309 | int newSize = offset + 1; |
| 310 | if(newSize >= page.length) resizePage(newSize); |
| 311 | |
| 312 | page[ offset++ ] = (byte)(value ? 1 : 0); |
| 313 | } |
| 314 | |
| 315 | boolean readBoolean(){ |
| 316 | return page[ offset++ ] != 0; |
| 317 | } |
| 318 | |
| 319 | void writeShort( int value ){ |
| 320 | int newSize = offset + 2; |
| 321 | if(newSize >= page.length) resizePage(newSize); |
| 322 | |
| 323 | page[ offset++ ] = (byte)(value >> 8); |
| 324 | page[ offset++ ] = (byte)(value); |
| 325 | } |
| 326 | |
| 327 | int readShort(){ |
| 328 | return (page[ offset++ ] << 8) | (page[ offset++ ] & 0xFF); |
| 329 | } |
| 330 | |
| 331 | void writeInt( int value ){ |
| 332 | int newSize = offset + 4; |
| 333 | if(newSize >= page.length) resizePage(newSize); |
| 334 | |
| 335 | page[ offset++ ] = (byte)(value >> 24); |
| 336 | page[ offset++ ] = (byte)(value >> 16); |
| 337 | page[ offset++ ] = (byte)(value >> 8); |
| 338 | page[ offset++ ] = (byte)(value); |
| 339 | } |
| 340 | |
| 341 | int readInt(){ |
| 342 | return ((page[ offset++ ]) << 24) | |
| 343 | ((page[ offset++ ] & 0xFF) << 16) | |
| 344 | ((page[ offset++ ] & 0xFF) << 8) | |
| 345 | ((page[ offset++ ] & 0xFF)); |
| 346 | } |
| 347 | |
| 348 | void writeLong( long value ){ |
| 349 | int newSize = offset + 8; |
| 350 | if(newSize >= page.length) resizePage(newSize); |
| 351 | |
| 352 | page[ offset++ ] = (byte)(value >> 56); |
| 353 | page[ offset++ ] = (byte)(value >> 48); |
| 354 | page[ offset++ ] = (byte)(value >> 40); |
| 355 | page[ offset++ ] = (byte)(value >> 32); |
| 356 | page[ offset++ ] = (byte)(value >> 24); |
| 357 | page[ offset++ ] = (byte)(value >> 16); |
| 358 | page[ offset++ ] = (byte)(value >> 8); |
| 359 | page[ offset++ ] = (byte)(value); |
| 360 | } |
| 361 | |
| 362 | long readLong(){ |
| 363 | //return (((long)readInt()) << 32) | (readInt() & 0xFFFFFFFFL); |
| 364 | return ((long)(page[ offset++ ]) << 56) | |
| 365 | ((long)(page[ offset++ ] & 0xFF) << 48) | |
| 366 | ((long)(page[ offset++ ] & 0xFF) << 40) | |
| 367 | ((long)(page[ offset++ ] & 0xFF) << 32) | |
| 368 | ((long)(page[ offset++ ] & 0xFF) << 24) | |
| 369 | ((page[ offset++ ] & 0xFF) << 16) | |
| 370 | ((page[ offset++ ] & 0xFF) << 8) | |
| 371 | ((page[ offset++ ] & 0xFF)); |
| 372 | } |
| 373 | |
| 374 | void writeDouble(double value){ |
| 375 | writeLong( Double.doubleToLongBits(value) ); |
| 376 | } |
| 377 | |
| 378 | double readDouble(){ |
| 379 | return Double.longBitsToDouble( readLong() ); |
| 380 | } |
| 381 | |
| 382 | void writeFloat(float value){ |
| 383 | writeInt( Float.floatToIntBits(value) ); |
| 384 | } |
| 385 | |
| 386 | float readFloat(){ |
| 387 | return Float.intBitsToFloat( readInt() ); |
| 388 | } |
| 389 | |
| 390 | void writeNumeric( MutableNumeric num){ |
| 391 | writeByte( num.value.length ); |
| 392 | writeByte( num.scale ); |
| 393 | writeByte( num.signum ); |
| 394 | for(int i=0; i<num.value.length; i++){ |
| 395 | writeInt( num.value[i] ); |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | MutableNumeric readNumeric(){ |
| 400 | int[] value = new int[ readByte() ]; |
| 401 | int scale = readByte(); |
| 402 | int signum = readByte(); |
| 403 | for(int i=0; i<value.length; i++){ |
| 404 | value[i] = readInt(); |
| 405 | } |
| 406 | return new MutableNumeric( signum, value, scale ); |
| 407 | } |
| 408 | |
| 409 | void writeTimestamp( long ts){ |
| 410 | writeLong( ts ); |
| 411 | } |
| 412 | |
| 413 | long readTimestamp(){ |
| 414 | return readLong(); |
| 415 | } |
| 416 | |
| 417 | void writeTime( long time){ |
| 418 | writeInt( (int)((time / 1000) % 86400) ); |
| 419 | } |
| 420 | |
| 421 | long readTime(){ |
| 422 | return readInt() * 1000L; |
| 423 | } |
| 424 | |
| 425 | void writeDate( long date){ |
| 426 | writeInt( (int)(date / 86400000)); |
| 427 | } |
| 428 | |
| 429 | long readDate(){ |
| 430 | return readInt() * 86400000L; |
| 431 | } |
| 432 | |
| 433 | void writeSmallDateTime( long datetime){ |
| 434 | writeInt( (int)(datetime / 60000)); |
| 435 | } |
| 436 | |
| 437 | long readSmallDateTime(){ |
| 438 | return readInt() * 60000L; |
| 439 | } |
| 440 | |
| 441 | void writeString( String strDaten ) throws SQLException{ |
| 442 | writeString( strDaten, Short.MAX_VALUE, true ); |
| 443 | } |
| 444 | |
| 445 | void writeString( String strDaten, int lengthColumn, boolean varchar ) throws SQLException{ |
| 446 | char[] daten = strDaten.toCharArray(); |
| 447 | int length = daten.length; |
| 448 | |
| 449 | if(lengthColumn < length){ |
| 450 | throw Utils.createSQLException("String value to large for column."); |
| 451 | } |
| 452 | if(varchar) lengthColumn = length; |
| 453 | int newSize = offset + 2 + 2*lengthColumn; |
| 454 | if(newSize >= page.length) resizePage(newSize); |
| 455 | |
| 456 | writeShort( lengthColumn ); |
| 457 | writeChars( daten ); |
| 458 | for(int i=length; i<lengthColumn; i++){ |
| 459 | page[ offset++ ] = ' '; |
| 460 | page[ offset++ ] = 0; |
| 461 | } |
| 462 | } |
| 463 | |
| 464 | String readString(){ |
| 465 | int length = readShort(); |
| 466 | return new String( readChars(length) ); |
| 467 | } |
| 468 | |
| 469 | void writeBytes(byte[] daten){ |
| 470 | System.arraycopy( daten, 0, page, offset, daten.length); |
| 471 | offset += daten.length; |
| 472 | } |
| 473 | |
| 474 | byte[] readBytes(int length){ |
| 475 | byte[] daten = new byte[length]; |
| 476 | System.arraycopy( page, offset, daten, 0, length); |
| 477 | offset += length; |
| 478 | return daten; |
| 479 | } |
| 480 | |
| 481 | void writeBinary( byte[] daten, int lengthColumn, boolean varBinary ) throws SQLException{ |
| 482 | int length = daten.length; |
| 483 | |
| 484 | if(lengthColumn < length){ |
| 485 | throw Utils.createSQLException("Binary value with length "+length+" to large for column with size "+lengthColumn+"."); |
| 486 | } |
| 487 | if(varBinary) lengthColumn = length; |
| 488 | int newSize = offset + 2 + lengthColumn; |
| 489 | if(newSize >= page.length) resizePage(newSize); |
| 490 | |
| 491 | page[ offset++ ] = (byte)(lengthColumn >> 8); |
| 492 | page[ offset++ ] = (byte)(lengthColumn); |
| 493 | writeBytes( daten ); |
| 494 | if(!varBinary){ |
| 495 | for(int i=length; i<lengthColumn; i++){ |
| 496 | page[ offset++ ] = 0; |
| 497 | } |
| 498 | } |
| 499 | } |
| 500 | |
| 501 | byte[] readBinary(){ |
| 502 | int length = readShort(); |
| 503 | return readBytes(length); |
| 504 | } |
| 505 | |
| 506 | void writeLongBinary( byte[] daten ) throws Exception{ |
| 507 | StoreImpl store = table.getLobStore( ((TableStorePage)storePage).con, daten.length + 4, SQLTokenizer.LONGVARBINARY); |
| 508 | store.writeInt( daten.length ); |
| 509 | store.writeBytes( daten ); |
| 510 | writeLong( store.writeFinsh(null) ); |
| 511 | } |
| 512 | |
| 513 | byte[] readLongBinary() throws Exception{ |
| 514 | long filePos = readLong(); |
| 515 | StoreImpl store = table.getLobStore( ((TableStorePage)storePage).con, filePos, SQLTokenizer.SELECT ); |
| 516 | return store.readBytes( store.readInt() ); |
| 517 | } |
| 518 | |
| 519 | void writeChars(char[] daten){ |
| 520 | int length = daten.length; |
| 521 | for(int i=0; i<length; i++){ |
| 522 | char c = daten[i]; |
| 523 | page[ offset++ ] = (byte)(c); |
| 524 | page[ offset++ ] = (byte)(c >> 8); |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | char[] readChars(int length){ |
| 529 | char[] daten = new char[length]; |
| 530 | for(int i=0; i<length; i++){ |
| 531 | daten[i] = (char)((page[ offset++ ] & 0xFF) | (page[ offset++ ] << 8)); |
| 532 | } |
| 533 | return daten; |
| 534 | } |
| 535 | |
| 536 | void writeLongString(String daten) throws Exception{ |
| 537 | char[] chars = daten.toCharArray(); |
| 538 | StoreImpl store = table.getLobStore( ((TableStorePage)storePage).con, chars.length * 2L + 4, SQLTokenizer.LONGVARBINARY); |
| 539 | store.writeInt( chars.length ); |
| 540 | store.writeChars( chars ); |
| 541 | writeLong( store.writeFinsh(null) ); |
| 542 | } |
| 543 | |
| 544 | String readLongString() throws Exception{ |
| 545 | long filePos = readLong(); |
| 546 | StoreImpl store = table.getLobStore( ((TableStorePage)storePage).con, filePos, SQLTokenizer.SELECT ); |
| 547 | if(store == null) throw Utils.createSQLException("Lob Object was deleted."); |
| 548 | return new String(store.readChars( store.readInt() ) ); |
| 549 | } |
| 550 | |
| 551 | |
| 552 | void writeColumn( Table table, Column column ) throws Exception{ |
| 553 | int newSize = offset + 25; |
| 554 | if(newSize >= page.length) resizePage(newSize); |
| 555 | |
| 556 | writeByte ( column.getFlag() ); |
| 557 | writeString ( column.getName() ); |
| 558 | writeShort ( column.getDataType() ); |
| 559 | writeInt ( column.getPrecision() ); |
| 560 | writeByte ( column.getScale() ); |
| 561 | offset += column.initAutoIncrement( table, storePage.raFile, filePos+offset); |
| 562 | String def = column.getDefaultDefinition(); |
| 563 | writeBoolean( def == null ); |
| 564 | if(def != null) |
| 565 | writeString ( column.getDefaultDefinition() ); |
| 566 | } |
| 567 | |
| 568 | |
| 569 | /** |
| 570 | * Read a single Column description on the current file offset. |
| 571 | * @param table The parent table of the column. |
| 572 | * @param tableFormatVersion the file version of the table. |
| 573 | * @return |
| 574 | * @throws Exception |
| 575 | */ |
| 576 | Column readColumn(Table table, int tableFormatVersion) throws Exception{ |
| 577 | Column column = new Column(); |
| 578 | column.setFlag( readByte() ); |
| 579 | column.setName( readString() ); |
| 580 | column.setDataType( readShort() ); |
| 581 | int precision; |
| 582 | if(tableFormatVersion == TableView.TABLE_VIEW_OLD_VERSION) |
| 583 | precision = readByte(); |
| 584 | else |
| 585 | precision = readInt(); |
| 586 | column.setPrecision( precision ); |
| 587 | column.setScale( readByte() ); |
| 588 | offset += column.initAutoIncrement( table, storePage.raFile, filePos+offset); |
| 589 | if(!readBoolean()){ |
| 590 | String def = readString(); |
| 591 | column.setDefaultValue( new SQLParser().parseExpression(def), def); |
| 592 | } |
| 593 | return column; |
| 594 | } |
| 595 | |
| 596 | |
| 597 | void copyValueFrom( StoreImpl store, int offset, int length){ |
| 598 | System.arraycopy( store.page, offset, this.page, this.offset, length); |
| 599 | this.offset += length; |
| 600 | } |
| 601 | |
| 602 | // hier wird Expression übergeben anstelle von Object, um später |
| 603 | // Optimierungen duchzuführen ohne Object für einfache Daten. |
| 604 | // Der Wert von offset muß stimmen. |
| 605 | void writeExpression( Expression expr, Column column) throws Exception{ |
| 606 | boolean isNull = expr.isNull(); |
| 607 | if(isNull && !column.isNullable()){ |
| 608 | throw Utils.createSQLException("Null values are not valid for column '" + column.getName() + "'." ); |
| 609 | } |
| 610 | int dataType = column.getDataType(); |
| 611 | if(isNull){ |
| 612 | writeBoolean(true); //true - is null |
| 613 | switch(dataType){ |
| 614 | case SQLTokenizer.BIT: |
| 615 | case SQLTokenizer.BOOLEAN: |
| 616 | case SQLTokenizer.TINYINT: |
| 617 | offset++; |
| 618 | break; |
| 619 | case SQLTokenizer.SMALLINT: |
| 620 | case SQLTokenizer.BINARY: |
| 621 | case SQLTokenizer.VARBINARY: |
| 622 | case SQLTokenizer.CHAR: |
| 623 | case SQLTokenizer.NCHAR: |
| 624 | case SQLTokenizer.VARCHAR: |
| 625 | case SQLTokenizer.NVARCHAR: |
| 626 | offset += 2; |
| 627 | break; |
| 628 | case SQLTokenizer.INT: |
| 629 | case SQLTokenizer.REAL: |
| 630 | case SQLTokenizer.SMALLMONEY: |
| 631 | case SQLTokenizer.TIME: |
| 632 | case SQLTokenizer.DATE: |
| 633 | case SQLTokenizer.SMALLDATETIME: |
| 634 | offset += 4; |
| 635 | break; |
| 636 | case SQLTokenizer.BIGINT: |
| 637 | case SQLTokenizer.FLOAT: |
| 638 | case SQLTokenizer.DOUBLE: |
| 639 | case SQLTokenizer.MONEY: |
| 640 | case SQLTokenizer.JAVA_OBJECT: |
| 641 | case SQLTokenizer.LONGVARBINARY: |
| 642 | case SQLTokenizer.BLOB: |
| 643 | case SQLTokenizer.CLOB: |
| 644 | case SQLTokenizer.NCLOB: |
| 645 | case SQLTokenizer.LONGNVARCHAR: |
| 646 | case SQLTokenizer.LONGVARCHAR: |
| 647 | case SQLTokenizer.TIMESTAMP: |
| 648 | offset += 8; |
| 649 | break; |
| 650 | case SQLTokenizer.UNIQUEIDENTIFIER: |
| 651 | offset += 16; |
| 652 | break; |
| 653 | case SQLTokenizer.NUMERIC: |
| 654 | case SQLTokenizer.DECIMAL: |
| 655 | offset += 3; |
| 656 | break; |
| 657 | default: throw new Error(); |
| 658 | } |
| 659 | return; |
| 660 | } |
| 661 | writeBoolean(false); // false - is not null |
| 662 | column.setNewAutoIncrementValue(expr); |
| 663 | switch(dataType){ |
| 664 | case SQLTokenizer.BIT: |
| 665 | case SQLTokenizer.BOOLEAN: |
| 666 | writeBoolean( expr.getBoolean() ); |
| 667 | break; |
| 668 | case SQLTokenizer.BINARY: |
| 669 | case SQLTokenizer.VARBINARY: |
| 670 | writeBinary( expr.getBytes(), column.getPrecision(), dataType != SQLTokenizer.BINARY ); |
| 671 | break; |
| 672 | case SQLTokenizer.TINYINT: |
| 673 | writeByte( expr.getInt() ); |
| 674 | break; |
| 675 | case SQLTokenizer.SMALLINT: |
| 676 | writeShort( expr.getInt() ); |
| 677 | break; |
| 678 | case SQLTokenizer.INT: |
| 679 | writeInt( expr.getInt() ); |
| 680 | break; |
| 681 | case SQLTokenizer.BIGINT: |
| 682 | writeLong( expr.getLong() ); |
| 683 | break; |
| 684 | case SQLTokenizer.REAL: |
| 685 | writeFloat( expr.getFloat() ); |
| 686 | break; |
| 687 | case SQLTokenizer.FLOAT: |
| 688 | case SQLTokenizer.DOUBLE: |
| 689 | writeDouble( expr.getDouble() ); |
| 690 | break; |
| 691 | case SQLTokenizer.MONEY: |
| 692 | writeLong( expr.getMoney() ); |
| 693 | break; |
| 694 | case SQLTokenizer.SMALLMONEY: |
| 695 | writeInt( (int)expr.getMoney() ); |
| 696 | break; |
| 697 | case SQLTokenizer.NUMERIC: |
| 698 | case SQLTokenizer.DECIMAL: |
| 699 | MutableNumeric numeric = expr.getNumeric(); |
| 700 | numeric.setScale( column.getScale() ); |
| 701 | writeNumeric( numeric ); |
| 702 | break; |
| 703 | case SQLTokenizer.CHAR: |
| 704 | case SQLTokenizer.NCHAR: |
| 705 | writeString( expr.getString(), column.getDisplaySize(), false ); |
| 706 | break; |
| 707 | case SQLTokenizer.VARCHAR: |
| 708 | case SQLTokenizer.NVARCHAR: |
| 709 | writeString( expr.getString(), column.getDisplaySize(), true ); |
| 710 | break; |
| 711 | case SQLTokenizer.CLOB: |
| 712 | case SQLTokenizer.NCLOB: |
| 713 | case SQLTokenizer.LONGNVARCHAR: |
| 714 | case SQLTokenizer.LONGVARCHAR: |
| 715 | writeLongString( expr.getString() ); |
| 716 | break; |
| 717 | case SQLTokenizer.JAVA_OBJECT: |
| 718 | // Fixme ein MemoryStream könnte schneller sein |
| 719 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| 720 | ObjectOutputStream oos = new ObjectOutputStream(baos); |
| 721 | oos.writeObject( expr.getObject() ); |
| 722 | writeLongBinary( baos.toByteArray() ); |
| 723 | break; |
| 724 | case SQLTokenizer.LONGVARBINARY: |
| 725 | case SQLTokenizer.BLOB: |
| 726 | writeLongBinary( expr.getBytes() ); |
| 727 | break; |
| 728 | case SQLTokenizer.TIMESTAMP: |
| 729 | writeTimestamp( expr.getLong() ); |
| 730 | break; |
| 731 | case SQLTokenizer.TIME: |
| 732 | writeTime( expr.getLong() ); |
| 733 | break; |
| 734 | case SQLTokenizer.DATE: |
| 735 | writeDate( expr.getLong() ); |
| 736 | break; |
| 737 | case SQLTokenizer.SMALLDATETIME: |
| 738 | writeSmallDateTime( expr.getLong() ); |
| 739 | break; |
| 740 | case SQLTokenizer.UNIQUEIDENTIFIER: |
| 741 | switch(expr.getDataType()){ |
| 742 | case SQLTokenizer.UNIQUEIDENTIFIER: |
| 743 | case SQLTokenizer.BINARY: |
| 744 | case SQLTokenizer.VARBINARY: |
| 745 | case SQLTokenizer.LONGVARBINARY: |
| 746 | case SQLTokenizer.BLOB: |
| 747 | byte[] bytes = expr.getBytes(); |
| 748 | if(bytes.length != 16) throw Utils.createSQLException("Invalid byte array size:"+bytes.length); |
| 749 | writeBytes( bytes ); |
| 750 | default: |
| 751 | writeBytes( Utils.unique2bytes(expr.getString()) ); |
| 752 | } |
| 753 | break; |
| 754 | default: throw new Error(String.valueOf(column.getDataType())); |
| 755 | } |
| 756 | } |
| 757 | |
| 758 | boolean isNull(int offset){ |
| 759 | return page[ offset ] != 0; |
| 760 | } |
| 761 | |
| 762 | // wird für ResultSet.getBoolean() verwendet |
| 763 | boolean getBoolean( int offset, int dataType) throws Exception{ |
| 764 | this.offset = offset; |
| 765 | if(readBoolean()) return false; |
| 766 | switch(dataType){ |
| 767 | case SQLTokenizer.BIT: |
| 768 | case SQLTokenizer.BOOLEAN: |
| 769 | return readBoolean(); |
| 770 | case SQLTokenizer.BINARY: |
| 771 | case SQLTokenizer.VARBINARY: |
| 772 | return Utils.bytes2int( readBinary() ) != 0; |
| 773 | case SQLTokenizer.TINYINT: |
| 774 | return readUnsignedByte() != 0; |
| 775 | case SQLTokenizer.SMALLINT: |
| 776 | return readShort() != 0; |
| 777 | case SQLTokenizer.INT: |
| 778 | return readInt() != 0; |
| 779 | case SQLTokenizer.BIGINT: |
| 780 | return readLong() != 0; |
| 781 | case SQLTokenizer.REAL: |
| 782 | return readFloat() != 0; |
| 783 | case SQLTokenizer.FLOAT: |
| 784 | case SQLTokenizer.DOUBLE: |
| 785 | return readDouble() != 0; |
| 786 | case SQLTokenizer.MONEY: |
| 787 | return readLong() != 0; |
| 788 | case SQLTokenizer.SMALLMONEY: |
| 789 | return readInt() != 0; |
| 790 | case SQLTokenizer.NUMERIC: |
| 791 | case SQLTokenizer.DECIMAL: |
| 792 | return readNumeric().signum != 0; |
| 793 | case SQLTokenizer.CHAR: |
| 794 | case SQLTokenizer.NCHAR: |
| 795 | case SQLTokenizer.VARCHAR: |
| 796 | case SQLTokenizer.NVARCHAR: |
| 797 | return Utils.string2boolean( readString() ); |
| 798 | case SQLTokenizer.CLOB: |
| 799 | case SQLTokenizer.NCLOB: |
| 800 | case SQLTokenizer.LONGNVARCHAR: |
| 801 | case SQLTokenizer.LONGVARCHAR: |
| 802 | return Utils.string2boolean( readLongString() ); |
| 803 | case SQLTokenizer.JAVA_OBJECT: |
| 804 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 805 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 806 | return Utils.string2boolean( ois.readObject().toString() ); |
| 807 | case SQLTokenizer.LONGVARBINARY: |
| 808 | case SQLTokenizer.BLOB: |
| 809 | return Utils.bytes2int( readLongBinary() ) != 0; |
| 810 | case SQLTokenizer.TIMESTAMP: |
| 811 | return readTimestamp() != 0; |
| 812 | case SQLTokenizer.TIME: |
| 813 | return readTime() != 0; |
| 814 | case SQLTokenizer.DATE: |
| 815 | return readDate() != 0; |
| 816 | case SQLTokenizer.SMALLDATETIME: |
| 817 | return readSmallDateTime() != 0; |
| 818 | case SQLTokenizer.UNIQUEIDENTIFIER: |
| 819 | return false; |
| 820 | default: throw Utils.createSQLException("Cannot convert a " + SQLTokenizer.getKeyWord(dataType) + " value to a BOOLEAN value."); |
| 821 | } |
| 822 | } |
| 823 | |
| 824 | // wird für ResultSet.getInt() verwendet |
| 825 | int getInt( int offset, int dataType) throws Exception{ |
| 826 | this.offset = offset; |
| 827 | if(readBoolean()) return 0; |
| 828 | switch(dataType){ |
| 829 | case SQLTokenizer.BIT: |
| 830 | case SQLTokenizer.BOOLEAN: |
| 831 | return readBoolean() ? 1 : 0; |
| 832 | case SQLTokenizer.BINARY: |
| 833 | case SQLTokenizer.VARBINARY: |
| 834 | return Utils.bytes2int( readBinary() ); |
| 835 | case SQLTokenizer.TINYINT: |
| 836 | return readUnsignedByte(); |
| 837 | case SQLTokenizer.SMALLINT: |
| 838 | return readShort(); |
| 839 | case SQLTokenizer.INT: |
| 840 | return readInt(); |
| 841 | case SQLTokenizer.BIGINT: |
| 842 | return (int)readLong(); |
| 843 | case SQLTokenizer.REAL: |
| 844 | return (int)readFloat(); |
| 845 | case SQLTokenizer.FLOAT: |
| 846 | case SQLTokenizer.DOUBLE: |
| 847 | return (int)readDouble(); |
| 848 | case SQLTokenizer.MONEY: |
| 849 | return (int)(readLong() / 10000); |
| 850 | case SQLTokenizer.SMALLMONEY: |
| 851 | return readInt() / 10000; |
| 852 | case SQLTokenizer.NUMERIC: |
| 853 | case SQLTokenizer.DECIMAL: |
| 854 | return readNumeric().intValue(); |
| 855 | case SQLTokenizer.CHAR: |
| 856 | case SQLTokenizer.NCHAR: |
| 857 | case SQLTokenizer.VARCHAR: |
| 858 | case SQLTokenizer.NVARCHAR: |
| 859 | return Integer.parseInt( readString() ); |
| 860 | case SQLTokenizer.CLOB: |
| 861 | case SQLTokenizer.NCLOB: |
| 862 | case SQLTokenizer.LONGNVARCHAR: |
| 863 | case SQLTokenizer.LONGVARCHAR: |
| 864 | return Integer.parseInt( readLongString() ); |
| 865 | case SQLTokenizer.JAVA_OBJECT: |
| 866 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 867 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 868 | return ExpressionValue.getInt(ois.readObject().toString(), SQLTokenizer.VARCHAR); |
| 869 | case SQLTokenizer.LONGVARBINARY: |
| 870 | case SQLTokenizer.BLOB: |
| 871 | return Utils.bytes2int( readLongBinary() ); |
| 872 | case SQLTokenizer.TIMESTAMP: |
| 873 | return (int)readTimestamp(); |
| 874 | case SQLTokenizer.TIME: |
| 875 | return (int)readTime(); |
| 876 | case SQLTokenizer.DATE: |
| 877 | return (int)readDate(); |
| 878 | case SQLTokenizer.SMALLDATETIME: |
| 879 | return (int)readSmallDateTime(); |
| 880 | default: throw Utils.createSQLException("Cannot convert a " + SQLTokenizer.getKeyWord(dataType) + " value to a INT value."); |
| 881 | } |
| 882 | } |
| 883 | |
| 884 | // wird für ResultSet.getLong() verwendet |
| 885 | long getLong( int offset, int dataType) throws Exception{ |
| 886 | this.offset = offset; |
| 887 | if(readBoolean()) return 0; |
| 888 | switch(dataType){ |
| 889 | case SQLTokenizer.BIT: |
| 890 | case SQLTokenizer.BOOLEAN: |
| 891 | return readBoolean() ? 1 : 0; |
| 892 | case SQLTokenizer.BINARY: |
| 893 | case SQLTokenizer.VARBINARY: |
| 894 | return Utils.bytes2long( readBinary() ); |
| 895 | case SQLTokenizer.TINYINT: |
| 896 | return readUnsignedByte(); |
| 897 | case SQLTokenizer.SMALLINT: |
| 898 | return readShort(); |
| 899 | case SQLTokenizer.INT: |
| 900 | return readInt(); |
| 901 | case SQLTokenizer.BIGINT: |
| 902 | return readLong(); |
| 903 | case SQLTokenizer.REAL: |
| 904 | return (long)readFloat(); |
| 905 | case SQLTokenizer.FLOAT: |
| 906 | case SQLTokenizer.DOUBLE: |
| 907 | return (long)readDouble(); |
| 908 | case SQLTokenizer.MONEY: |
| 909 | return readLong() / 10000; |
| 910 | case SQLTokenizer.SMALLMONEY: |
| 911 | return readInt() / 10000; |
| 912 | case SQLTokenizer.NUMERIC: |
| 913 | case SQLTokenizer.DECIMAL: |
| 914 | return readNumeric().longValue(); |
| 915 | case SQLTokenizer.CHAR: |
| 916 | case SQLTokenizer.NCHAR: |
| 917 | case SQLTokenizer.VARCHAR: |
| 918 | case SQLTokenizer.NVARCHAR: |
| 919 | return Long.parseLong( readString() ); |
| 920 | case SQLTokenizer.CLOB: |
| 921 | case SQLTokenizer.NCLOB: |
| 922 | case SQLTokenizer.LONGNVARCHAR: |
| 923 | case SQLTokenizer.LONGVARCHAR: |
| 924 | return Long.parseLong( readLongString() ); |
| 925 | case SQLTokenizer.JAVA_OBJECT: |
| 926 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 927 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 928 | return ExpressionValue.getLong( ois.readObject().toString(), SQLTokenizer.VARCHAR ); |
| 929 | case SQLTokenizer.LONGVARBINARY: |
| 930 | case SQLTokenizer.BLOB: |
| 931 | return Utils.bytes2long( readLongBinary() ); |
| 932 | case SQLTokenizer.TIMESTAMP: |
| 933 | return readTimestamp(); |
| 934 | case SQLTokenizer.TIME: |
| 935 | return readTime(); |
| 936 | case SQLTokenizer.DATE: |
| 937 | return readDate(); |
| 938 | case SQLTokenizer.SMALLDATETIME: |
| 939 | return readSmallDateTime(); |
| 940 | default: throw Utils.createSQLException("Cannot convert a " + SQLTokenizer.getKeyWord(dataType) + " value to a BIGINT value."); |
| 941 | } |
| 942 | } |
| 943 | |
| 944 | // wird für ResultSet.getFloat() verwendet |
| 945 | float getFloat( int offset, int dataType) throws Exception{ |
| 946 | this.offset = offset; |
| 947 | if(readBoolean()) return 0; |
| 948 | switch(dataType){ |
| 949 | case SQLTokenizer.BIT: |
| 950 | case SQLTokenizer.BOOLEAN: |
| 951 | return readBoolean() ? 1 : 0; |
| 952 | case SQLTokenizer.BINARY: |
| 953 | case SQLTokenizer.VARBINARY: |
| 954 | return Utils.bytes2float( readBinary() ); |
| 955 | case SQLTokenizer.TINYINT: |
| 956 | return readUnsignedByte(); |
| 957 | case SQLTokenizer.SMALLINT: |
| 958 | return readShort(); |
| 959 | case SQLTokenizer.INT: |
| 960 | return readInt(); |
| 961 | case SQLTokenizer.BIGINT: |
| 962 | return readLong(); |
| 963 | case SQLTokenizer.REAL: |
| 964 | return readFloat(); |
| 965 | case SQLTokenizer.FLOAT: |
| 966 | case SQLTokenizer.DOUBLE: |
| 967 | return (float)readDouble(); |
| 968 | case SQLTokenizer.MONEY: |
| 969 | return readLong() / (float)10000.0; |
| 970 | case SQLTokenizer.SMALLMONEY: |
| 971 | return readInt() / (float)10000.0; |
| 972 | case SQLTokenizer.NUMERIC: |
| 973 | case SQLTokenizer.DECIMAL: |
| 974 | return readNumeric().floatValue(); |
| 975 | case SQLTokenizer.CHAR: |
| 976 | case SQLTokenizer.NCHAR: |
| 977 | case SQLTokenizer.VARCHAR: |
| 978 | case SQLTokenizer.NVARCHAR: |
| 979 | return Float.parseFloat( readString() ); |
| 980 | case SQLTokenizer.CLOB: |
| 981 | case SQLTokenizer.NCLOB: |
| 982 | case SQLTokenizer.LONGNVARCHAR: |
| 983 | case SQLTokenizer.LONGVARCHAR: |
| 984 | return Float.parseFloat( readLongString() ); |
| 985 | case SQLTokenizer.JAVA_OBJECT: |
| 986 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 987 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 988 | return Float.parseFloat( ois.readObject().toString() ); |
| 989 | case SQLTokenizer.LONGVARBINARY: |
| 990 | case SQLTokenizer.BLOB: |
| 991 | return Utils.bytes2float( readLongBinary() ); |
| 992 | case SQLTokenizer.TIMESTAMP: |
| 993 | return readTimestamp(); |
| 994 | case SQLTokenizer.TIME: |
| 995 | return readTime(); |
| 996 | case SQLTokenizer.DATE: |
| 997 | return readDate(); |
| 998 | case SQLTokenizer.SMALLDATETIME: |
| 999 | return readSmallDateTime(); |
| 1000 | default: throw Utils.createSQLException("Cannot convert a " + SQLTokenizer.getKeyWord(dataType) + " value to a REAL value."); |
| 1001 | } |
| 1002 | } |
| 1003 | |
| 1004 | // wird für ResultSet.getDouble() verwendet |
| 1005 | double getDouble( int offset, int dataType) throws Exception{ |
| 1006 | this.offset = offset; |
| 1007 | if(readBoolean()) return 0; |
| 1008 | switch(dataType){ |
| 1009 | case SQLTokenizer.BIT: |
| 1010 | case SQLTokenizer.BOOLEAN: |
| 1011 | return readBoolean() ? 1 : 0; |
| 1012 | case SQLTokenizer.BINARY: |
| 1013 | case SQLTokenizer.VARBINARY: |
| 1014 | return Utils.bytes2double( readBinary() ); |
| 1015 | case SQLTokenizer.TINYINT: |
| 1016 | return readUnsignedByte(); |
| 1017 | case SQLTokenizer.SMALLINT: |
| 1018 | return readShort(); |
| 1019 | case SQLTokenizer.INT: |
| 1020 | return readInt(); |
| 1021 | case SQLTokenizer.BIGINT: |
| 1022 | return readLong(); |
| 1023 | case SQLTokenizer.REAL: |
| 1024 | return readFloat(); |
| 1025 | case SQLTokenizer.FLOAT: |
| 1026 | case SQLTokenizer.DOUBLE: |
| 1027 | return readDouble(); |
| 1028 | case SQLTokenizer.MONEY: |
| 1029 | return readLong() / 10000.0; |
| 1030 | case SQLTokenizer.SMALLMONEY: |
| 1031 | return readInt() / 10000.0; |
| 1032 | case SQLTokenizer.NUMERIC: |
| 1033 | case SQLTokenizer.DECIMAL: |
| 1034 | return readNumeric().doubleValue(); |
| 1035 | case SQLTokenizer.CHAR: |
| 1036 | case SQLTokenizer.NCHAR: |
| 1037 | case SQLTokenizer.VARCHAR: |
| 1038 | case SQLTokenizer.NVARCHAR: |
| 1039 | return Double.parseDouble( readString() ); |
| 1040 | case SQLTokenizer.CLOB: |
| 1041 | case SQLTokenizer.NCLOB: |
| 1042 | case SQLTokenizer.LONGNVARCHAR: |
| 1043 | case SQLTokenizer.LONGVARCHAR: |
| 1044 | return Double.parseDouble( readLongString() ); |
| 1045 | case SQLTokenizer.JAVA_OBJECT: |
| 1046 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 1047 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 1048 | return Double.parseDouble( ois.readObject().toString() ); |
| 1049 | case SQLTokenizer.LONGVARBINARY: |
| 1050 | case SQLTokenizer.BLOB: |
| 1051 | return Utils.bytes2double( readLongBinary() ); |
| 1052 | case SQLTokenizer.TIMESTAMP: |
| 1053 | return readTimestamp(); |
| 1054 | case SQLTokenizer.TIME: |
| 1055 | return readTime(); |
| 1056 | case SQLTokenizer.DATE: |
| 1057 | return readDate(); |
| 1058 | case SQLTokenizer.SMALLDATETIME: |
| 1059 | return readSmallDateTime(); |
| 1060 | default: throw Utils.createSQLException("Cannot convert a " + SQLTokenizer.getKeyWord(dataType) + " value to a DOUBLE value."); |
| 1061 | } |
| 1062 | } |
| 1063 | |
| 1064 | // wird zum schnelleren Rechnen verwendet |
| 1065 | long getMoney( int offset, int dataType) throws Exception{ |
| 1066 | this.offset = offset; |
| 1067 | if(readBoolean()) return 0; |
| 1068 | switch(dataType){ |
| 1069 | case SQLTokenizer.BIT: |
| 1070 | case SQLTokenizer.BOOLEAN: |
| 1071 | return readBoolean() ? 10000 : 0; |
| 1072 | case SQLTokenizer.BINARY: |
| 1073 | case SQLTokenizer.VARBINARY: |
| 1074 | return (long)(Utils.bytes2double( readBinary() ) * 10000L); |
| 1075 | case SQLTokenizer.TINYINT: |
| 1076 | return readUnsignedByte() * 10000L; |
| 1077 | case SQLTokenizer.SMALLINT: |
| 1078 | return readShort() * 10000L; |
| 1079 | case SQLTokenizer.INT: |
| 1080 | return readInt() * 10000L; |
| 1081 | case SQLTokenizer.BIGINT: |
| 1082 | return readLong() * 10000L; |
| 1083 | case SQLTokenizer.REAL: |
| 1084 | return (long)(readFloat() * 10000L); |
| 1085 | case SQLTokenizer.FLOAT: |
| 1086 | case SQLTokenizer.DOUBLE: |
| 1087 | return (long)(readDouble() * 10000L); |
| 1088 | case SQLTokenizer.MONEY: |
| 1089 | return readLong(); |
| 1090 | case SQLTokenizer.SMALLMONEY: |
| 1091 | return readInt(); |
| 1092 | case SQLTokenizer.NUMERIC: |
| 1093 | case SQLTokenizer.DECIMAL: |
| 1094 | return (long)(readNumeric().doubleValue() * 10000L); |
| 1095 | case SQLTokenizer.CHAR: |
| 1096 | case SQLTokenizer.NCHAR: |
| 1097 | case SQLTokenizer.VARCHAR: |
| 1098 | case SQLTokenizer.NVARCHAR: |
| 1099 | return Money.parseMoney( readString() ); |
| 1100 | case SQLTokenizer.CLOB: |
| 1101 | case SQLTokenizer.NCLOB: |
| 1102 | case SQLTokenizer.LONGNVARCHAR: |
| 1103 | case SQLTokenizer.LONGVARCHAR: |
| 1104 | return Money.parseMoney( readLongString() ); |
| 1105 | case SQLTokenizer.JAVA_OBJECT: |
| 1106 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 1107 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 1108 | return Money.parseMoney( ois.readObject().toString() ); |
| 1109 | case SQLTokenizer.LONGVARBINARY: |
| 1110 | case SQLTokenizer.BLOB: |
| 1111 | return (long)(Utils.bytes2double( readLongBinary() ) * 10000L); |
| 1112 | case SQLTokenizer.TIMESTAMP: |
| 1113 | case SQLTokenizer.TIME: |
| 1114 | case SQLTokenizer.DATE: |
| 1115 | case SQLTokenizer.SMALLDATETIME: |
| 1116 | throw Utils.createSQLException("Cannot convert a " + SQLTokenizer.getKeyWord(dataType) + " value to a MONEY value."); |
| 1117 | default: throw new Error(); |
| 1118 | } |
| 1119 | } |
| 1120 | |
| 1121 | // wird für ResultSet.getBigDecimal() verwendet |
| 1122 | MutableNumeric getNumeric( int offset, int dataType) throws Exception{ |
| 1123 | this.offset = offset; |
| 1124 | if(readBoolean()) return null; |
| 1125 | switch(dataType){ |
| 1126 | case SQLTokenizer.BIT: |
| 1127 | case SQLTokenizer.BOOLEAN: |
| 1128 | return readBoolean() ? new MutableNumeric(1) : new MutableNumeric(0); |
| 1129 | case SQLTokenizer.BINARY: |
| 1130 | case SQLTokenizer.VARBINARY: |
| 1131 | return new MutableNumeric(Utils.bytes2double( readBinary() )); |
| 1132 | case SQLTokenizer.TINYINT: |
| 1133 | return new MutableNumeric(readUnsignedByte()); |
| 1134 | case SQLTokenizer.SMALLINT: |
| 1135 | return new MutableNumeric(readShort()); |
| 1136 | case SQLTokenizer.INT: |
| 1137 | return new MutableNumeric(readInt()); |
| 1138 | case SQLTokenizer.BIGINT: |
| 1139 | return new MutableNumeric(readLong()); |
| 1140 | case SQLTokenizer.REAL: |
| 1141 | return new MutableNumeric(readFloat()); |
| 1142 | case SQLTokenizer.FLOAT: |
| 1143 | case SQLTokenizer.DOUBLE: |
| 1144 | return new MutableNumeric(readDouble()); |
| 1145 | case SQLTokenizer.MONEY: |
| 1146 | return new MutableNumeric( readLong(), 4); |
| 1147 | case SQLTokenizer.SMALLMONEY: |
| 1148 | return new MutableNumeric( readInt(), 4); |
| 1149 | case SQLTokenizer.NUMERIC: |
| 1150 | case SQLTokenizer.DECIMAL: |
| 1151 | return readNumeric(); |
| 1152 | case SQLTokenizer.CHAR: |
| 1153 | case SQLTokenizer.NCHAR: |
| 1154 | case SQLTokenizer.VARCHAR: |
| 1155 | case SQLTokenizer.NVARCHAR: |
| 1156 | return new MutableNumeric( readString() ); |
| 1157 | case SQLTokenizer.CLOB: |
| 1158 | case SQLTokenizer.NCLOB: |
| 1159 | case SQLTokenizer.LONGNVARCHAR: |
| 1160 | case SQLTokenizer.LONGVARCHAR: |
| 1161 | return new MutableNumeric( readLongString() ); |
| 1162 | case SQLTokenizer.JAVA_OBJECT: |
| 1163 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 1164 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 1165 | return new MutableNumeric( ois.readObject().toString() ); |
| 1166 | case SQLTokenizer.LONGVARBINARY: |
| 1167 | case SQLTokenizer.BLOB: |
| 1168 | return new MutableNumeric( Utils.bytes2double( readLongBinary() ) ); |
| 1169 | case SQLTokenizer.TIMESTAMP: |
| 1170 | case SQLTokenizer.TIME: |
| 1171 | case SQLTokenizer.DATE: |
| 1172 | case SQLTokenizer.SMALLDATETIME: |
| 1173 | throw Utils.createSQLException("Cannot convert a " + SQLTokenizer.getKeyWord(dataType) + " value to a NUMERIC value."); |
| 1174 | default: throw new Error(); |
| 1175 | } |
| 1176 | } |
| 1177 | |
| 1178 | |
| 1179 | |
| 1180 | // wird für ResultSet.getObject() verwendet |
| 1181 | Object getObject( int offset, int dataType) throws Exception{ |
| 1182 | this.offset = offset; |
| 1183 | if(readBoolean()) return null; |
| 1184 | switch(dataType){ |
| 1185 | case SQLTokenizer.BIT: |
| 1186 | case SQLTokenizer.BOOLEAN: |
| 1187 | return readBoolean() ? Boolean.TRUE : Boolean.FALSE; |
| 1188 | case SQLTokenizer.BINARY: |
| 1189 | case SQLTokenizer.VARBINARY: |
| 1190 | return readBinary(); |
| 1191 | case SQLTokenizer.TINYINT: |
| 1192 | return Utils.getInteger( readUnsignedByte() ); |
| 1193 | case SQLTokenizer.SMALLINT: |
| 1194 | return Utils.getInteger( readShort() ); |
| 1195 | case SQLTokenizer.INT: |
| 1196 | return Utils.getInteger(readInt()); |
| 1197 | case SQLTokenizer.BIGINT: |
| 1198 | return new Long(readLong()); |
| 1199 | case SQLTokenizer.REAL: |
| 1200 | return new Float( readFloat() ); |
| 1201 | case SQLTokenizer.FLOAT: |
| 1202 | case SQLTokenizer.DOUBLE: |
| 1203 | return new Double( readDouble() ); |
| 1204 | case SQLTokenizer.MONEY: |
| 1205 | return Money.createFromUnscaledValue(readLong()); |
| 1206 | case SQLTokenizer.SMALLMONEY: |
| 1207 | return Money.createFromUnscaledValue(readInt()); |
| 1208 | case SQLTokenizer.NUMERIC: |
| 1209 | case SQLTokenizer.DECIMAL: |
| 1210 | return readNumeric(); |
| 1211 | case SQLTokenizer.CHAR: |
| 1212 | case SQLTokenizer.NCHAR: |
| 1213 | case SQLTokenizer.VARCHAR: |
| 1214 | case SQLTokenizer.NVARCHAR: |
| 1215 | return readString(); |
| 1216 | case SQLTokenizer.CLOB: |
| 1217 | case SQLTokenizer.NCLOB: |
| 1218 | case SQLTokenizer.LONGNVARCHAR: |
| 1219 | case SQLTokenizer.LONGVARCHAR: |
| 1220 | return readLongString(); |
| 1221 | case SQLTokenizer.JAVA_OBJECT: |
| 1222 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 1223 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 1224 | return ois.readObject(); |
| 1225 | case SQLTokenizer.LONGVARBINARY: |
| 1226 | case SQLTokenizer.BLOB: |
| 1227 | return readLongBinary(); |
| 1228 | case SQLTokenizer.TIMESTAMP: |
| 1229 | return new DateTime( readTimestamp(), SQLTokenizer.TIMESTAMP ); |
| 1230 | case SQLTokenizer.TIME: |
| 1231 | return new DateTime( readTime(), SQLTokenizer.TIME ); |
| 1232 | case SQLTokenizer.DATE: |
| 1233 | return new DateTime( readDate(), SQLTokenizer.DATE ); |
| 1234 | case SQLTokenizer.SMALLDATETIME: |
| 1235 | return new DateTime( readSmallDateTime(), SQLTokenizer.TIMESTAMP ); |
| 1236 | case SQLTokenizer.UNIQUEIDENTIFIER: |
| 1237 | return Utils.bytes2unique( page, this.offset); |
| 1238 | default: throw new Error(); |
| 1239 | } |
| 1240 | } |
| 1241 | |
| 1242 | // wird für ResultSet.getString() verwendet |
| 1243 | String getString( int offset, int dataType) throws Exception{ |
| 1244 | this.offset = offset; |
| 1245 | if(readBoolean()) return null; |
| 1246 | switch(dataType){ |
| 1247 | case SQLTokenizer.BIT: |
| 1248 | return readBoolean() ? "1" : "0"; |
| 1249 | case SQLTokenizer.BOOLEAN: |
| 1250 | return String.valueOf( readBoolean() ); |
| 1251 | case SQLTokenizer.BINARY: |
| 1252 | case SQLTokenizer.VARBINARY: |
| 1253 | return Utils.bytes2hex( readBinary() ); |
| 1254 | case SQLTokenizer.TINYINT: |
| 1255 | return String.valueOf( readUnsignedByte() ); |
| 1256 | case SQLTokenizer.SMALLINT: |
| 1257 | return String.valueOf( readShort() ); |
| 1258 | case SQLTokenizer.INT: |
| 1259 | return String.valueOf( readInt() ); |
| 1260 | case SQLTokenizer.BIGINT: |
| 1261 | return String.valueOf( readLong() ); |
| 1262 | case SQLTokenizer.REAL: |
| 1263 | return String.valueOf( readFloat() ); |
| 1264 | case SQLTokenizer.FLOAT: |
| 1265 | case SQLTokenizer.DOUBLE: |
| 1266 | return String.valueOf( readDouble() ); |
| 1267 | case SQLTokenizer.MONEY: |
| 1268 | return Money.createFromUnscaledValue( readLong() ).toString(); |
| 1269 | case SQLTokenizer.SMALLMONEY: |
| 1270 | return Money.createFromUnscaledValue( readInt() ).toString(); |
| 1271 | case SQLTokenizer.NUMERIC: |
| 1272 | case SQLTokenizer.DECIMAL: |
| 1273 | return readNumeric().toString(); |
| 1274 | case SQLTokenizer.CHAR: |
| 1275 | case SQLTokenizer.NCHAR: |
| 1276 | case SQLTokenizer.VARCHAR: |
| 1277 | case SQLTokenizer.NVARCHAR: |
| 1278 | return readString(); |
| 1279 | case SQLTokenizer.CLOB: |
| 1280 | case SQLTokenizer.NCLOB: |
| 1281 | case SQLTokenizer.LONGNVARCHAR: |
| 1282 | case SQLTokenizer.LONGVARCHAR: |
| 1283 | return readLongString(); |
| 1284 | case SQLTokenizer.JAVA_OBJECT: |
| 1285 | ByteArrayInputStream bais = new ByteArrayInputStream(readLongBinary()); |
| 1286 | ObjectInputStream ois = new ObjectInputStream(bais); |
| 1287 | return ois.readObject().toString(); |
| 1288 | case SQLTokenizer.LONGVARBINARY: |
| 1289 | case SQLTokenizer.BLOB: |
| 1290 | return Utils.bytes2hex( readLongBinary() ); |
| 1291 | case SQLTokenizer.TIMESTAMP: |
| 1292 | return new DateTime( readTimestamp(), SQLTokenizer.TIMESTAMP ).toString(); |
| 1293 | case SQLTokenizer.TIME: |
| 1294 | return new DateTime( readTime(), SQLTokenizer.TIME ).toString(); |
| 1295 | case SQLTokenizer.DATE: |
| 1296 | return new DateTime( readDate(), SQLTokenizer.DATE ).toString(); |
| 1297 | case SQLTokenizer.SMALLDATETIME: |
| 1298 | return new DateTime( readSmallDateTime(), SQLTokenizer.TIMESTAMP ).toString(); |
| 1299 | case SQLTokenizer.UNIQUEIDENTIFIER: |
| 1300 | return Utils.bytes2unique( page, this.offset); |
| 1301 | default: throw new Error(); |
| 1302 | } |
| 1303 | } |
| 1304 | |
| 1305 | // wird für ResultSet.getBytes() verwendet |
| 1306 | byte[] getBytes( int offset, int dataType) throws Exception{ |
| 1307 | this.offset = offset; |
| 1308 | if(readBoolean()) return null; |
| 1309 | switch(dataType){ |
| 1310 | case SQLTokenizer.BINARY: |
| 1311 | case SQLTokenizer.VARBINARY: |
| 1312 | return readBinary(); |
| 1313 | case SQLTokenizer.TINYINT: |
| 1314 | case SQLTokenizer.BIT: |
| 1315 | case SQLTokenizer.BOOLEAN: |
| 1316 | byte[] bytes = new byte[1]; |
| 1317 | System.arraycopy( page, offset, bytes, 0, bytes.length); |
| 1318 | return bytes; |
| 1319 | case SQLTokenizer.SMALLINT: |
| 1320 | bytes = new byte[2]; |
| 1321 | System.arraycopy( page, offset, bytes, 0, bytes.length); |
| 1322 | return bytes; |
| 1323 | case SQLTokenizer.INT: |
| 1324 | case SQLTokenizer.REAL: |
| 1325 | case SQLTokenizer.SMALLMONEY: |
| 1326 | case SQLTokenizer.TIME: |
| 1327 | case SQLTokenizer.DATE: |
| 1328 | case SQLTokenizer.SMALLDATETIME: |
| 1329 | bytes = new byte[4]; |
| 1330 | System.arraycopy( page, offset, bytes, 0, bytes.length); |
| 1331 | return bytes; |
| 1332 | case SQLTokenizer.BIGINT: |
| 1333 | case SQLTokenizer.FLOAT: |
| 1334 | case SQLTokenizer.DOUBLE: |
| 1335 | case SQLTokenizer.MONEY: |
| 1336 | case SQLTokenizer.TIMESTAMP: |
| 1337 | bytes = new byte[8]; |
| 1338 | System.arraycopy( page, offset, bytes, 0, bytes.length); |
| 1339 | return bytes; |
| 1340 | case SQLTokenizer.NUMERIC: |
| 1341 | case SQLTokenizer.DECIMAL: |
| 1342 | return readNumeric().toByteArray(); |
| 1343 | case SQLTokenizer.CHAR: |
| 1344 | case SQLTokenizer.NCHAR: |
| 1345 | case SQLTokenizer.VARCHAR: |
| 1346 | case SQLTokenizer.NVARCHAR: |
| 1347 | return readString().getBytes(); |
| 1348 | case SQLTokenizer.CLOB: |
| 1349 | case SQLTokenizer.NCLOB: |
| 1350 | case SQLTokenizer.LONGNVARCHAR: |
| 1351 | case SQLTokenizer.LONGVARCHAR: |
| 1352 | return readLongString().getBytes(); |
| 1353 | case SQLTokenizer.JAVA_OBJECT: |
| 1354 | case SQLTokenizer.LONGVARBINARY: |
| 1355 | case SQLTokenizer.BLOB: |
| 1356 | return readLongBinary(); |
| 1357 | case SQLTokenizer.UNIQUEIDENTIFIER: |
| 1358 | bytes = new byte[16]; |
| 1359 | System.arraycopy( page, offset, bytes, 0, bytes.length); |
| 1360 | return bytes; |
| 1361 | default: throw new Error(); |
| 1362 | } |
| 1363 | } |
| 1364 | |
| 1365 | void scanObjectOffsets( int[] offsets, int dataTypes[] ){ |
| 1366 | offset = PAGE_CONTROL_SIZE; |
| 1367 | for(int i=0; i<offsets.length; i++){ |
| 1368 | offsets[i] = offset; |
| 1369 | boolean isNull = readBoolean(); // für isNull |
| 1370 | switch(dataTypes[i]){ |
| 1371 | case SQLTokenizer.BIT: |
| 1372 | case SQLTokenizer.BOOLEAN: |
| 1373 | case SQLTokenizer.TINYINT: |
| 1374 | offset++; |
| 1375 | break; |
| 1376 | case SQLTokenizer.SMALLINT: |
| 1377 | offset += 2; |
| 1378 | break; |
| 1379 | case SQLTokenizer.INT: |
| 1380 | case SQLTokenizer.REAL: |
| 1381 | case SQLTokenizer.SMALLMONEY: |
| 1382 | case SQLTokenizer.TIME: |
| 1383 | case SQLTokenizer.DATE: |
| 1384 | case SQLTokenizer.SMALLDATETIME: |
| 1385 | offset += 4; |
| 1386 | break; |
| 1387 | case SQLTokenizer.BIGINT: |
| 1388 | case SQLTokenizer.FLOAT: |
| 1389 | case SQLTokenizer.DOUBLE: |
| 1390 | case SQLTokenizer.MONEY: |
| 1391 | case SQLTokenizer.JAVA_OBJECT: |
| 1392 | case SQLTokenizer.LONGVARBINARY: |
| 1393 | case SQLTokenizer.BLOB: |
| 1394 | case SQLTokenizer.CLOB: |
| 1395 | case SQLTokenizer.NCLOB: |
| 1396 | case SQLTokenizer.LONGNVARCHAR: |
| 1397 | case SQLTokenizer.LONGVARCHAR: |
| 1398 | case SQLTokenizer.TIMESTAMP: |
| 1399 | offset += 8; |
| 1400 | break; |
| 1401 | case SQLTokenizer.BINARY: |
| 1402 | case SQLTokenizer.VARBINARY: |
| 1403 | int count = readShort(); |
| 1404 | if(!isNull) offset += count; // nicht += verwenden, weil readShort offset verändert |
| 1405 | break; |
| 1406 | case SQLTokenizer.NUMERIC: |
| 1407 | case SQLTokenizer.DECIMAL: |
| 1408 | count = readByte(); |
| 1409 | offset += 2; |
| 1410 | if(!isNull) offset += count*4; |
| 1411 | break; |
| 1412 | case SQLTokenizer.CHAR: |
| 1413 | case SQLTokenizer.NCHAR: |
| 1414 | case SQLTokenizer.VARCHAR: |
| 1415 | case SQLTokenizer.NVARCHAR: |
| 1416 | count = readShort(); |
| 1417 | if(!isNull) offset += count << 1; // nicht += verwenden, weil readShort offset verändert |
| 1418 | break; |
| 1419 | case SQLTokenizer.UNIQUEIDENTIFIER: |
| 1420 | offset += 16; |
| 1421 | break; |
| 1422 | default: throw new Error(String.valueOf( dataTypes[i] ) ); |
| 1423 | } |
| 1424 | } |
| 1425 | } |
| 1426 | |
| 1427 | void deleteRow(SSConnection con) throws SQLException{ |
| 1428 | status = DELETED; |
| 1429 | type = SQLTokenizer.DELETE; |
| 1430 | createWriteLock(); |
| 1431 | writeFinsh(con); |
| 1432 | } |
| 1433 | |
| 1434 | |
| 1435 | StorePageLink getLink(){ |
| 1436 | return ((TableStorePageInsert)storePage).getLink(); |
| 1437 | } |
| 1438 | } |