package com.alibaba.doris.dataserver.store.innodb.db; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.alibaba.doris.common.data.CompareStatus; import com.alibaba.doris.common.data.Key; import com.alibaba.doris.common.data.Pair; import com.alibaba.doris.common.data.Value; import com.alibaba.doris.common.data.ValueFactory; import com.alibaba.doris.common.data.impl.PairImpl; import com.alibaba.doris.dataserver.store.ClosableIterator; import com.alibaba.doris.dataserver.store.StorageType; import com.alibaba.doris.dataserver.store.exception.VersionConflictException; import com.g414.haildb.Cursor; import com.g414.haildb.Cursor.LockMode; import com.g414.haildb.Cursor.SearchMode; import com.g414.haildb.Database; import com.g414.haildb.TableDef; import com.g414.haildb.Transaction; import com.g414.haildb.Transaction.TransactionLevel; import com.g414.haildb.Transaction.TransactionState; import com.g414.haildb.Tuple; /** * @author long.mal long.mal@alibaba-inc.com */ public class InnoDBDataBase extends InnoDBBase { public InnoDBDataBase(InnoDBBuilder dbBuilder, String databaseName) { this.dbBuilder = dbBuilder; this.database = dbBuilder.getDatabase(); this.databaseName = databaseName; } public void close() { // ingore; } public void open() { tableDef = dbBuilder.buildTable(databaseName); } public String getName() { return this.databaseName; } public int getNodeIndex() { return Integer.parseInt(this.databaseName); } public TableDef getTableDef() { return this.tableDef; } public boolean delete(Key key) { return delete(key, null); } public boolean delete(List<Integer> vnodeList) { throw new UnsupportedOperationException("The operation of 'delete' is not supported!", null); } public boolean delete(final Key key, final Value value) { final byte[] keyBytes = serializerFactory.encode(key).copyBytes(); Transaction txn = null; Cursor c = null; Tuple toDelete = null; Tuple toFind = null; try { txn = database.beginTransaction(TransactionLevel.REPEATABLE_READ); c = txn.openTable(tableDef); c.setLockMode(LockMode.INTENTION_EXCLUSIVE); c.lock(LockMode.LOCK_EXCLUSIVE); final Map<String, Object> searchKey = new HashMap<String, Object>(); searchKey.put("key_", keyBytes); toFind = c.createClusteredIndexSearchTuple(searchKey); c.find(toFind, SearchMode.GE); if (c != null && c.isPositioned() && c.hasNext()) { toDelete = c.createClusteredIndexReadTuple(); c.readRow(toDelete); Map<String, Object> found = toDelete.valueMap(); if (getKeyStringFromResult(found).equals(new String(keyBytes))) { Value oldValue = ValueFactory.createValue(null, getVersionFromResult(found)); if (value != null) { CompareStatus status = oldValue.compareVersion(value); if (status == CompareStatus.AFTER) { throw new VersionConflictException("Key " + key.getKey() + " Namespace " + key.getNamespace() + " " + value.getTimestamp() + " is obsolete, it is no greater than the current version of " + oldValue.getTimestamp() + "."); } } c.deleteRow(); return true; } } return false; } catch (Exception e) { throw new RuntimeException(e); } finally { if (toDelete != null) { toDelete.clear(); toDelete.delete(); } if (toFind != null) { toFind.clear(); toFind.delete(); } if (c != null) { c.close(); } if (txn != null) { if (txn.getState().equals(TransactionState.NOT_STARTED)) { txn.release(); } else { txn.commit(); } } } } public Value get(final Key key) { final byte[] keyBytes = serializerFactory.encode(key).copyBytes(); final Map<String, Object> searchKey = new HashMap<String, Object>(); searchKey.put("key_", keyBytes); Transaction txn = null; Cursor c0 = null; Tuple tuple = null; try { txn = database.beginTransaction(TransactionLevel.READ_COMMITTED); c0 = txn.openTable(tableDef); tuple = c0.createClusteredIndexSearchTuple(searchKey); c0.find(tuple, SearchMode.GE); if (c0 != null && c0.isPositioned() && c0.hasNext()) { Tuple read = c0.createClusteredIndexReadTuple(); try { c0.readRow(read); Map<String, Object> row = read.valueMap(); if (getKeyStringFromResult(row).equals(new String(keyBytes))) return getValueFromResult(row); } catch (Exception e) { } finally { read.delete(); if (c0 != null) c0.next(); } } return null; } catch (Exception e) { throw new RuntimeException(e); } finally { if (c0 != null) { c0.close(); c0 = null; } if (tuple != null) { tuple.delete(); } if (txn != null) { if (txn.getState().equals(TransactionState.NOT_STARTED)) { txn.release(); } else { txn.commit(); } } } } public void set(final Key key, final Value value) { this.set(key, value, false); } public void put(final Key key, final Value value) { this.set(key, value); } public void set(final Key key, final Value value, final boolean isSetWithCompareVersion) { final byte[] keyBytes = serializerFactory.encode(key).copyBytes(); final byte[] valueBytes = serializerFactory.encode(value).copyBytes(); Transaction txn = null; Cursor c = null; Tuple toInsert = null; Tuple toUpdate = null; Tuple toFind = null; try { txn = database.beginTransaction(TransactionLevel.READ_COMMITTED); Map<String, Object> newRow = new LinkedHashMap<String, Object>(); newRow.put(InnoDBBuilder.FIELD_KEY, keyBytes); newRow.put(InnoDBBuilder.FIELD_VALUE, valueBytes); newRow.put(InnoDBBuilder.FIELD_VERSION, value.getTimestamp()); c = txn.openTable(tableDef); c.setLockMode(LockMode.INTENTION_EXCLUSIVE); c.lock(LockMode.LOCK_EXCLUSIVE); final Map<String, Object> searchKey = new HashMap<String, Object>(); searchKey.put(InnoDBBuilder.FIELD_KEY, keyBytes); toFind = c.createClusteredIndexSearchTuple(searchKey); c.find(toFind, SearchMode.GE); if (c != null && c.isPositioned() && c.hasNext()) { toUpdate = c.createClusteredIndexReadTuple(); c.readRow(toUpdate); Map<String, Object> found = toUpdate.valueMap(); if (getKeyStringFromResult(found).equals(new String(keyBytes))) { Value oldValue = ValueFactory.createValue(null, getVersionFromResult(found)); CompareStatus status = oldValue.compareVersion(value); if (status == CompareStatus.AFTER) { throw new VersionConflictException("Key " + key.getKey() + " Namespace " + key.getNamespace() + " " + value.getTimestamp() + " is obsolete, it is no greater than the current version of " + oldValue.getTimestamp() + "."); } c.updateRow(toUpdate, newRow); return; } } toInsert = c.createClusteredIndexReadTuple(); c.insertRow(toInsert, newRow); return; } catch (Exception e) { throw new RuntimeException(e); } finally { if (toInsert != null) { toInsert.delete(); } if (toUpdate != null) { toUpdate.delete(); } if (toFind != null) { toFind.delete(); } if (c != null) { c.close(); } if (txn != null) { if (txn.getState().equals(TransactionState.NOT_STARTED)) { txn.release(); } else { txn.commit(); } } } } public StorageType getType() { return null; } public void traverse() throws Exception { Transaction txn = null; Cursor cursor = null; try { txn = database.beginTransaction(TransactionLevel.READ_COMMITTED); cursor = txn.openTable(tableDef); cursor.first(); while (cursor != null && cursor.isPositioned() && cursor.hasNext()) { Tuple read = cursor.createClusteredIndexReadTuple(); try { cursor.readRow(read); // Map<String, Object> row = read.valueMap(); // System.out.println(row.get("key_")); } finally { read.delete(); cursor.next(); } } } catch (Exception e) { if (txn != null) { if (txn.getState().equals(TransactionState.NOT_STARTED)) { txn.release(); } else { txn.rollback(); } txn = null; } throw new RuntimeException(e); } finally { if (cursor != null) { cursor.close(); } if (txn != null) { if (txn.getState().equals(TransactionState.NOT_STARTED)) { txn.release(); } else { txn.commit(); } } } } public Iterator<Pair> iterator() { Transaction txn = null; Cursor cursor = null; try { txn = database.beginTransaction(TransactionLevel.READ_COMMITTED); cursor = txn.openTable(tableDef); cursor.first(); return new InnoDBCursorIterator<Pair>(txn, cursor); } catch (Exception e) { if (cursor != null) { cursor.close(); cursor = null; } if (txn != null) { if (txn.getState().equals(TransactionState.NOT_STARTED)) { txn.release(); } else { txn.rollback(); } txn = null; } throw new RuntimeException(e); } finally { } } public Iterator<Pair> iterator(List<Integer> vnodeList) { return iterator(); } private static class InnoDBCursorIterator<T> implements ClosableIterator<T> { final Transaction txn; private Cursor cursor; private boolean closed = false; public InnoDBCursorIterator(Transaction txn, Cursor c) { this.txn = txn; this.cursor = c; } public boolean hasNext() { boolean hasNext = (cursor != null && cursor.isPositioned() && cursor.hasNext()); if (!hasNext) { this.close(); } return hasNext; } public T next() { if (cursor == null) { return null; } Tuple read = cursor.createClusteredIndexReadTuple(); try { cursor.readRow(read); Map<String, Object> row = read.valueMap(); Key key = serializerFactory.decodeKey((byte[]) row.get(InnoDBBuilder.FIELD_KEY)); Value value = serializerFactory.decodeValue((byte[]) row.get(InnoDBBuilder.FIELD_VALUE)); return (T) new PairImpl(key, value); } finally { read.delete(); cursor.next(); } } public void remove() { throw new UnsupportedOperationException(); } public void close() { if (closed) { return; } if (cursor != null) { cursor.close(); cursor = null; } if (txn != null) { if (txn.getState().equals(TransactionState.NOT_STARTED)) { txn.release(); } else { txn.commit(); } } closed = true; } } private InnoDBBuilder dbBuilder; private TableDef tableDef; private Database database; private String databaseName; }