99import java .util .HashMap ;
1010import java .util .HashSet ;
1111import java .util .Iterator ;
12+ import java .util .LinkedList ;
1213import java .util .Random ;
1314
1415import org .h2 .api .ErrorCode ;
@@ -108,6 +109,20 @@ public class Session extends SessionWithState {
108109 private final int queryCacheSize ;
109110 private SmallLRUCache <String , Command > queryCache ;
110111 private long modificationMetaID = -1 ;
112+
113+ /**
114+ * Temporary LOBs from result sets. Those are kept for some time. The
115+ * problem is that transactions are committed before the result is returned,
116+ * and in some cases the next transaction is already started before the
117+ * result is read (for example when using the server mode, when accessing
118+ * metadata methods). We can't simply free those values up when starting the
119+ * next transaction, because they would be removed too early.
120+ */
121+ private LinkedList <TimeoutValue > temporaryResultLobs ;
122+
123+ /**
124+ * The temporary LOBs that need to be removed on commit.
125+ */
111126 private ArrayList <Value > temporaryLobs ;
112127
113128 private Transaction transaction ;
@@ -497,14 +512,7 @@ public void commit(boolean ddl) {
497512 // (create/drop table and so on)
498513 database .commit (this );
499514 }
500- if (temporaryLobs != null ) {
501- for (Value v : temporaryLobs ) {
502- if (!v .isLinked ()) {
503- v .close ();
504- }
505- }
506- temporaryLobs .clear ();
507- }
515+ removeTemporaryLobs (true );
508516 if (undoLog .size () > 0 ) {
509517 // commit the rows when using MVCC
510518 if (database .isMultiVersion ()) {
@@ -536,6 +544,31 @@ public void commit(boolean ddl) {
536544 endTransaction ();
537545 }
538546
547+ private void removeTemporaryLobs (boolean onTimeout ) {
548+ if (temporaryLobs != null ) {
549+ for (Value v : temporaryLobs ) {
550+ if (!v .isLinked ()) {
551+ v .close ();
552+ }
553+ }
554+ temporaryLobs .clear ();
555+ }
556+ if (temporaryResultLobs != null && temporaryResultLobs .size () > 0 ) {
557+ long keepYoungerThan = System .currentTimeMillis () -
558+ database .getSettings ().lobTimeout ;
559+ while (temporaryResultLobs .size () > 0 ) {
560+ TimeoutValue tv = temporaryResultLobs .getFirst ();
561+ if (onTimeout && tv .created >= keepYoungerThan ) {
562+ break ;
563+ }
564+ Value v = temporaryResultLobs .removeFirst ().value ;
565+ if (!v .isLinked ()) {
566+ v .close ();
567+ }
568+ }
569+ }
570+ }
571+
539572 private void checkCommitRollback () {
540573 if (commitOrRollbackDisabled && locks .size () > 0 ) {
541574 throw DbException .get (ErrorCode .COMMIT_ROLLBACK_NOT_ALLOWED );
@@ -545,8 +578,8 @@ private void checkCommitRollback() {
545578 private void endTransaction () {
546579 if (unlinkLobMap != null && unlinkLobMap .size () > 0 ) {
547580 if (database .getMvStore () == null ) {
548- // need to flush the transaction log, because we can't unlink lobs
549- // if the commit record is not written
581+ // need to flush the transaction log, because we can't unlink
582+ // lobs if the commit record is not written
550583 database .flush ();
551584 }
552585 for (Value v : unlinkLobMap .values ()) {
@@ -673,6 +706,7 @@ public void close() {
673706 if (!closed ) {
674707 try {
675708 database .checkPowerOff ();
709+ removeTemporaryLobs (false );
676710 cleanTempTables (true );
677711 undoLog .clear ();
678712 database .removeSession (this );
@@ -1447,10 +1481,17 @@ public void endStatement() {
14471481
14481482 @ Override
14491483 public void addTemporaryLob (Value v ) {
1450- if (temporaryLobs == null ) {
1451- temporaryLobs = new ArrayList <Value >();
1484+ if (v .getTableId () == LobStorageFrontend .TABLE_RESULT ) {
1485+ if (temporaryResultLobs == null ) {
1486+ temporaryResultLobs = new LinkedList <TimeoutValue >();
1487+ }
1488+ temporaryResultLobs .add (new TimeoutValue (v ));
1489+ } else {
1490+ if (temporaryLobs == null ) {
1491+ temporaryLobs = new ArrayList <Value >();
1492+ }
1493+ temporaryLobs .add (v );
14521494 }
1453- temporaryLobs .add (v );
14541495 }
14551496
14561497 /**
@@ -1470,4 +1511,25 @@ public static class Savepoint {
14701511 long transactionSavepoint ;
14711512 }
14721513
1514+ /**
1515+ * An object with a timeout.
1516+ */
1517+ public static class TimeoutValue {
1518+
1519+ /**
1520+ * The time when this object was created.
1521+ */
1522+ final long created = System .currentTimeMillis ();
1523+
1524+ /**
1525+ * The value.
1526+ */
1527+ final Value value ;
1528+
1529+ TimeoutValue (Value v ) {
1530+ this .value = v ;
1531+ }
1532+
1533+ }
1534+
14731535}
0 commit comments