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 | } |