1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.store.raw.xact.TransactionTableEntry |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.store.raw.xact; |
22 | |
23 | import org.apache.derby.iapi.services.sanity.SanityManager; |
24 | import org.apache.derby.iapi.services.io.Formatable; |
25 | import org.apache.derby.iapi.services.io.FormatIdUtil; |
26 | import org.apache.derby.iapi.services.io.StoredFormatIds; |
27 | |
28 | import org.apache.derby.iapi.error.StandardException; |
29 | import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; |
30 | import org.apache.derby.iapi.sql.conn.StatementContext; |
31 | import org.apache.derby.iapi.store.access.TransactionInfo; |
32 | import org.apache.derby.iapi.store.raw.GlobalTransactionId; |
33 | import org.apache.derby.iapi.store.raw.xact.TransactionId; |
34 | import org.apache.derby.iapi.store.raw.log.LogInstant; |
35 | |
36 | import java.io.ObjectOutput; |
37 | import java.io.ObjectInput; |
38 | import java.io.IOException; |
39 | |
40 | /** |
41 | Transaction table entry is used to store all relavent information of a |
42 | transaction into the transaction table for the use of checkpoint, recovery, |
43 | Transaction management during Quiesce state, and for dumping transaction table. Only works |
44 | with the following classes: TransactionTable, XactFactory, Xact |
45 | <BR> |
46 | During run time, whenever any transaction is started, it is put into the |
47 | transaction table. Whenever any transaction is closed, it is removed from |
48 | the transaction table. |
49 | |
50 | |
51 | */ |
52 | |
53 | public class TransactionTableEntry implements Formatable, TransactionInfo, Cloneable |
54 | { |
55 | // These fields are only populated if this TTE has been read in from the |
56 | // log. Otherwise, they are gotten from the transaction object myxact. |
57 | private TransactionId xid; |
58 | private GlobalTransactionId gid; |
59 | private LogInstant firstLog; |
60 | private LogInstant lastLog; |
61 | |
62 | // this field is always present - it is 0 for read only transaction, this |
63 | // is a copy of the status from the Xact (the copy is necessary as during |
64 | // recovery the Xact is shared by all transaction table entries during |
65 | // redo and undo). |
66 | private int transactionStatus; |
67 | |
68 | // fields useful for returning transaction information if read from |
69 | // transaction log during recovery |
70 | private transient Xact myxact; |
71 | private transient boolean update; |
72 | private transient boolean recovery; // is this a transaction read |
73 | // from the log during recovery? |
74 | private transient boolean needExclusion; // in a quiesce state , this |
75 | // transaction needs to be |
76 | // barred from activation |
77 | // during quiesce state |
78 | |
79 | private boolean isClone; // am I a clone made for the |
80 | // TransactionVTI? |
81 | |
82 | private transient LanguageConnectionContext lcc; |
83 | |
84 | |
85 | /* package */ |
86 | // entry attribute |
87 | static final int UPDATE = 0x1; |
88 | static final int RECOVERY = 0x2; |
89 | static final int EXCLUDE = 0x4; |
90 | |
91 | TransactionTableEntry( |
92 | Xact xact, |
93 | TransactionId tid, |
94 | int status, |
95 | int attribute) |
96 | { |
97 | myxact = xact; |
98 | xid = tid; |
99 | transactionStatus = status; |
100 | |
101 | update = (attribute & UPDATE) != 0; |
102 | needExclusion = (attribute & EXCLUDE) != 0; |
103 | recovery = (attribute & RECOVERY) != 0; |
104 | |
105 | if (SanityManager.DEBUG) |
106 | { |
107 | SanityManager.ASSERT(tid != null, "tid is null"); |
108 | if (update && xact.getFirstLogInstant() == null) |
109 | { |
110 | SanityManager.THROWASSERT( |
111 | "update transaction has firstLog = null"); |
112 | } |
113 | |
114 | |
115 | /* |
116 | if (!update && xact.getFirstLogInstant() != null) |
117 | { |
118 | SanityManager.THROWASSERT( |
119 | "read only transaction has firstLog = " + |
120 | xact.getFirstLogInstant()); |
121 | } |
122 | */ |
123 | } |
124 | |
125 | // Normally, we don't need to remember the gid, firstLog and lastLog |
126 | // because myxact will have the same information. However, in |
127 | // recovery, there is only one transaction taking on different identity |
128 | // as the log is replayed. Then each transaction table entry has keep |
129 | // its own identity and not rely on myxact. These recovery |
130 | // transactions are materialized in the transaction table via a |
131 | // readObject in the checkpoint log record, or are added by |
132 | // addUpdateTransaction when the log is scanned. |
133 | if (recovery) |
134 | { |
135 | // make a copy of everything |
136 | if (SanityManager.DEBUG) |
137 | { |
138 | SanityManager.ASSERT(update, "recovery but not update"); |
139 | |
140 | if (tid != xact.getId()) |
141 | { |
142 | SanityManager.THROWASSERT( |
143 | "adding a update transaction during recovery " + |
144 | " but the tids doesn't match" + |
145 | tid + " " + xact.getId()); |
146 | } |
147 | } |
148 | |
149 | gid = xact.getGlobalId(); |
150 | firstLog = xact.getFirstLogInstant(); |
151 | lastLog = xact.getLastLogInstant(); |
152 | } |
153 | } |
154 | |
155 | /* |
156 | * Formatable methods |
157 | */ |
158 | public TransactionTableEntry() |
159 | { } |
160 | |
161 | public void writeExternal(ObjectOutput out) throws IOException |
162 | { |
163 | if (SanityManager.DEBUG) |
164 | { |
165 | SanityManager.ASSERT(!recovery, "writing out a recovery transaction"); |
166 | SanityManager.ASSERT(update, "writing out read only transaction"); |
167 | SanityManager.ASSERT(myxact.getFirstLogInstant() != null, |
168 | "myxact.getFirstLogInstant is null"); |
169 | SanityManager.ASSERT(!isClone, "cannot write out a clone"); |
170 | } |
171 | |
172 | // Why is is safe to access first and last log instant in myxact while |
173 | // this is happening? Because we only writes out update transaction |
174 | // during run time. When a read only transactions becomes an update |
175 | // transaction , or when an update transaction commits, the beginXact |
176 | // and endXact log record's doMe method will try to change the |
177 | // transaction table entry's state to updat and non-update |
178 | // respectively. That change needs to go thru the transaction table |
179 | // which is mutually exclusive to writing out the transaction table. |
180 | // Since we are only looking at update transactions and it is "stuck" |
181 | // in update state in the middle of a TransactionTable.writeExternal |
182 | // call, all the fields we access in myxact is stable (actually the xid |
183 | // is also stable but we already have it). |
184 | // |
185 | out.writeObject(xid); |
186 | out.writeObject(myxact.getGlobalId()); |
187 | out.writeObject(myxact.getFirstLogInstant()); |
188 | out.writeObject(myxact.getLastLogInstant()); |
189 | out.writeInt(transactionStatus); |
190 | } |
191 | |
192 | public void readExternal(ObjectInput in) |
193 | throws ClassNotFoundException, IOException |
194 | { |
195 | // the only time a transaction table entry is written out is to the |
196 | // log, so this must be read in during recovery. |
197 | if (SanityManager.DEBUG) |
198 | SanityManager.ASSERT(!isClone, "cannot write out a clone"); |
199 | |
200 | xid = (TransactionId)in.readObject(); |
201 | gid = (GlobalTransactionId)in.readObject(); |
202 | firstLog = (LogInstant)in.readObject(); |
203 | lastLog = (LogInstant)in.readObject(); |
204 | transactionStatus = in.readInt(); |
205 | update = true; |
206 | recovery = true; |
207 | needExclusion = true; |
208 | |
209 | if (SanityManager.DEBUG) |
210 | { |
211 | SanityManager.ASSERT(xid != null, "read in transaction table entry with null id"); |
212 | SanityManager.ASSERT(firstLog != null, "read in transaction table entry with firstLog"); |
213 | } |
214 | |
215 | } |
216 | |
217 | |
218 | // set my transaction instance variable for a recovery transaction |
219 | void setXact(Xact xact) |
220 | { |
221 | /* |
222 | RESOLVE (mikem) - prepared transactions now call setXact() when they are |
223 | not in recovery. |
224 | if (SanityManager.DEBUG) |
225 | { |
226 | SanityManager.ASSERT(recovery, |
227 | "setting non-recovery transaction table entry xact"); |
228 | SanityManager.ASSERT(!isClone, "cannot setXact with a clone"); |
229 | } |
230 | */ |
231 | myxact = xact; |
232 | |
233 | } |
234 | |
235 | /** |
236 | Return my format identifier. |
237 | */ |
238 | public int getTypeFormatId() { |
239 | return StoredFormatIds.RAW_STORE_TRANSACTION_TABLE_ENTRY; |
240 | } |
241 | |
242 | public String toString() |
243 | { |
244 | if (SanityManager.DEBUG) |
245 | { |
246 | StringBuffer str = new StringBuffer(500). |
247 | append("Xid=").append(getXid()). |
248 | append(" gid=").append(getGid()). |
249 | append(" firstLog=").append(getFirstLog()). |
250 | append(" lastLog=").append(getLastLog()). |
251 | append(" transactionStatus=").append(transactionStatus). |
252 | append(" myxact=").append(myxact). |
253 | append(" update=").append(update). |
254 | append(" recovery=").append(recovery). |
255 | append(" prepare=").append(isPrepared()). |
256 | append(" needExclusion=").append(needExclusion). |
257 | append("\n"); |
258 | return str.toString(); |
259 | } |
260 | else |
261 | return null; |
262 | } |
263 | |
264 | void updateTransactionStatus(Xact xact, int status, int attribute) |
265 | { |
266 | if (SanityManager.DEBUG) |
267 | { |
268 | SanityManager.ASSERT(myxact == xact, |
269 | "update transaction status for wrong xact"); |
270 | SanityManager.ASSERT(!isClone, "cannot change a clone"); |
271 | } |
272 | |
273 | this.update = (attribute & UPDATE) != 0; |
274 | } |
275 | |
276 | void removeUpdateTransaction() |
277 | { |
278 | if (SanityManager.DEBUG) |
279 | SanityManager.ASSERT(!isClone, "cannot change a clone"); |
280 | |
281 | this.update = false; |
282 | transactionStatus = 0; |
283 | |
284 | } |
285 | |
286 | void unsetRecoveryStatus() |
287 | { |
288 | if (SanityManager.DEBUG) |
289 | SanityManager.ASSERT(!isClone, "cannot change a clone"); |
290 | |
291 | // RESOLVE (mikem) - this is kind of ugly. move to a better place? |
292 | |
293 | firstLog = null; |
294 | |
295 | this.recovery = false; |
296 | } |
297 | |
298 | void prepareTransaction() |
299 | { |
300 | if (SanityManager.DEBUG) |
301 | SanityManager.ASSERT(!isClone, "cannot change a clone"); |
302 | |
303 | transactionStatus |= Xact.END_PREPARED; |
304 | } |
305 | |
306 | /************************************************************************** |
307 | * get instance variables |
308 | ************************************************************************** |
309 | */ |
310 | |
311 | TransactionId getXid() |
312 | { |
313 | if (SanityManager.DEBUG) |
314 | { |
315 | SanityManager.ASSERT(xid != null, "TTE with null xid"); |
316 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
317 | } |
318 | |
319 | return xid; |
320 | } |
321 | |
322 | public final GlobalTransactionId getGid() |
323 | { |
324 | if (SanityManager.DEBUG) |
325 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
326 | |
327 | if (gid != null) |
328 | return gid; |
329 | |
330 | if (myxact != null) |
331 | return myxact.getGlobalId(); |
332 | |
333 | return null; |
334 | } |
335 | |
336 | LogInstant getFirstLog() |
337 | { |
338 | if (SanityManager.DEBUG) |
339 | { |
340 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
341 | |
342 | if (recovery) |
343 | { |
344 | SanityManager.ASSERT( |
345 | firstLog != null, |
346 | "a recovery transaction with a null firstLog"); |
347 | } |
348 | else |
349 | { |
350 | SanityManager.ASSERT( |
351 | firstLog == null, |
352 | "a normal transaction with a non-null firstLog" + |
353 | "myxact.getFirstLogInstant() = " + myxact.getFirstLogInstant()); |
354 | } |
355 | } |
356 | |
357 | if (firstLog != null) |
358 | return firstLog; |
359 | |
360 | if (myxact != null) |
361 | return myxact.getFirstLogInstant(); |
362 | |
363 | return null; |
364 | } |
365 | |
366 | LogInstant getLastLog() |
367 | { |
368 | if (SanityManager.DEBUG) |
369 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
370 | |
371 | if (lastLog != null) |
372 | return lastLog; |
373 | |
374 | if (myxact != null) |
375 | return myxact.getLastLogInstant(); |
376 | |
377 | return null; |
378 | } |
379 | |
380 | public final Xact getXact() |
381 | { |
382 | if (SanityManager.DEBUG) |
383 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
384 | |
385 | return myxact; |
386 | } |
387 | |
388 | int getTransactionStatus() |
389 | { |
390 | if (SanityManager.DEBUG) |
391 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
392 | |
393 | return transactionStatus; |
394 | } |
395 | |
396 | boolean isUpdate() |
397 | { |
398 | if (SanityManager.DEBUG) |
399 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
400 | |
401 | return update; |
402 | } |
403 | |
404 | boolean isRecovery() |
405 | { |
406 | if (SanityManager.DEBUG) |
407 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
408 | |
409 | return recovery; |
410 | } |
411 | |
412 | boolean isPrepared() |
413 | { |
414 | if (SanityManager.DEBUG) |
415 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
416 | |
417 | return((transactionStatus & Xact.END_PREPARED) != 0); |
418 | } |
419 | |
420 | |
421 | |
422 | |
423 | public boolean needExclusion() |
424 | { |
425 | if (SanityManager.DEBUG) |
426 | SanityManager.ASSERT(!isClone, "cannot call method with a clone"); |
427 | |
428 | return needExclusion; |
429 | } |
430 | |
431 | /** |
432 | Methods of TransactionInfo |
433 | */ |
434 | public String getTransactionIdString() |
435 | { |
436 | if (SanityManager.DEBUG) |
437 | { |
438 | SanityManager.ASSERT( |
439 | !recovery, "trying to display recovery transaction"); |
440 | SanityManager.ASSERT(myxact != null, "my xact is null"); |
441 | SanityManager.ASSERT(isClone, "Should only call method on a clone"); |
442 | } |
443 | |
444 | TransactionId t = myxact.getIdNoCheck(); |
445 | return (t == null) ? "CLOSED" : t.toString(); |
446 | } |
447 | |
448 | public String getGlobalTransactionIdString() |
449 | { |
450 | if (SanityManager.DEBUG) |
451 | { |
452 | SanityManager.ASSERT( |
453 | !recovery, "trying to display recovery transaction"); |
454 | SanityManager.ASSERT(myxact != null, "my xact is null"); |
455 | SanityManager.ASSERT(isClone, "Should only call method on a clone"); |
456 | } |
457 | |
458 | GlobalTransactionId gid = myxact.getGlobalId(); |
459 | return (gid == null) ? null : gid.toString(); |
460 | } |
461 | |
462 | public String getUsernameString() |
463 | { |
464 | if (SanityManager.DEBUG) |
465 | SanityManager.ASSERT(isClone, "Should only call method on a clone"); |
466 | |
467 | getlcc(); |
468 | return (lcc == null) ? null : lcc.getAuthorizationId(); |
469 | } |
470 | |
471 | public String getTransactionTypeString() |
472 | { |
473 | if (SanityManager.DEBUG) |
474 | SanityManager.ASSERT(isClone, "Should only call method on a clone"); |
475 | |
476 | if (myxact == null) |
477 | return null; |
478 | else if (myxact.getTransName() != null) |
479 | return myxact.getTransName(); |
480 | else |
481 | return myxact.getContextId(); |
482 | } |
483 | |
484 | public String getTransactionStatusString() |
485 | { |
486 | if (SanityManager.DEBUG) |
487 | SanityManager.ASSERT(isClone, "Should only call method on a clone"); |
488 | |
489 | return (myxact == null) ? null : myxact.getState(); |
490 | } |
491 | |
492 | public String getStatementTextString() |
493 | { |
494 | if (SanityManager.DEBUG) |
495 | SanityManager.ASSERT(isClone, "Should only call method on a clone"); |
496 | |
497 | getlcc(); |
498 | if (lcc != null) |
499 | { |
500 | StatementContext sc = lcc.getStatementContext(); |
501 | if (sc != null) |
502 | return sc.getStatementText() ; |
503 | } |
504 | return null; |
505 | |
506 | } |
507 | |
508 | public String getFirstLogInstantString() |
509 | { |
510 | if (SanityManager.DEBUG) |
511 | SanityManager.ASSERT(isClone, "Should only call method on a clone"); |
512 | |
513 | LogInstant logInstant = |
514 | (myxact == null) ? null : myxact.getFirstLogInstant(); |
515 | |
516 | return (logInstant == null) ? null : logInstant.toString(); |
517 | |
518 | } |
519 | |
520 | private void getlcc() |
521 | { |
522 | if (SanityManager.DEBUG) |
523 | SanityManager.ASSERT(isClone, "Should only call method on a clone"); |
524 | |
525 | if (lcc == null && myxact != null && myxact.xc != null) |
526 | { |
527 | XactContext xc = myxact.xc; |
528 | |
529 | lcc = (LanguageConnectionContext) |
530 | xc.getContextManager().getContext( |
531 | LanguageConnectionContext.CONTEXT_ID); |
532 | } |
533 | } |
534 | |
535 | /** |
536 | Cloneable |
537 | */ |
538 | protected Object clone() |
539 | { |
540 | try |
541 | { |
542 | Object c = super.clone(); |
543 | ((TransactionTableEntry)c).isClone = true; |
544 | |
545 | return c; |
546 | } |
547 | catch (CloneNotSupportedException e) |
548 | { |
549 | // this should not happen, we are cloneable |
550 | if (SanityManager.DEBUG) |
551 | { |
552 | SanityManager.THROWASSERT( |
553 | "TransactionTableEntry cloneable but throws CloneNotSupportedException " + e); |
554 | } |
555 | return null; |
556 | } |
557 | } |
558 | } |