Example #1
0
 /**
  * Parse and prepare the given SQL statement. This method also checks if the connection has been
  * closed.
  *
  * @param sql the SQL statement
  * @return the prepared statement
  */
 public Command prepareLocal(String sql) {
   if (closed) {
     throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "session closed");
   }
   Command command;
   if (queryCacheSize > 0) {
     if (queryCache == null) {
       queryCache = SmallLRUCache.newInstance(queryCacheSize);
       modificationMetaID = database.getModificationMetaId();
     } else {
       long newModificationMetaID = database.getModificationMetaId();
       if (newModificationMetaID != modificationMetaID) {
         queryCache.clear();
         modificationMetaID = newModificationMetaID;
       }
       command = queryCache.get(sql);
       if (command != null && command.canReuse()) {
         command.reuse();
         return command;
       }
     }
   }
   Parser parser = new Parser(this);
   command = parser.prepareCommand(sql);
   if (queryCache != null) {
     if (command.isCacheable()) {
       queryCache.put(sql, command);
     }
   }
   return command;
 }
Example #2
0
 /**
  * Get or create a trace object for this module.
  *
  * @param module the module name
  * @return the trace object
  */
 public synchronized Trace getTrace(String module) {
   Trace t = traces.get(module);
   if (t == null) {
     t = new Trace(writer, module);
     traces.put(module, t);
   }
   return t;
 }
Example #3
0
 public PlanItem getBestPlanItem(Session session, int[] masks) {
   PlanItem item = new PlanItem();
   item.cost = index.getCost(session, masks);
   IntArray masksArray = new IntArray(masks == null ? Utils.EMPTY_INT_ARRAY : masks);
   ViewIndex i2 = indexCache.get(masksArray);
   if (i2 == null || i2.getSession() != session) {
     i2 = new ViewIndex(this, index, session, masks);
     indexCache.put(masksArray, i2);
   }
   item.setIndex(i2);
   return item;
 }
Example #4
0
 public double getCost(Session session, int[] masks) {
   if (recursive) {
     return 1000;
   }
   IntArray masksArray = new IntArray(masks == null ? Utils.EMPTY_INT_ARRAY : masks);
   CostElement cachedCost = costCache.get(masksArray);
   if (cachedCost != null) {
     long time = System.currentTimeMillis();
     if (time < cachedCost.evaluatedAt + Constants.VIEW_COST_CACHE_MAX_AGE) {
       return cachedCost.cost;
     }
   }
   Query q = (Query) session.prepare(querySQL, true);
   if (masks != null) {
     IntArray paramIndex = new IntArray();
     for (int i = 0; i < masks.length; i++) {
       int mask = masks[i];
       if (mask == 0) {
         continue;
       }
       paramIndex.add(i);
     }
     int len = paramIndex.size();
     for (int i = 0; i < len; i++) {
       int idx = paramIndex.get(i);
       int mask = masks[idx];
       int nextParamIndex = q.getParameters().size() + view.getParameterOffset();
       if ((mask & IndexCondition.EQUALITY) != 0) {
         Parameter param = new Parameter(nextParamIndex);
         q.addGlobalCondition(param, idx, Comparison.EQUAL_NULL_SAFE);
       } else {
         if ((mask & IndexCondition.START) != 0) {
           Parameter param = new Parameter(nextParamIndex);
           q.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
         }
         if ((mask & IndexCondition.END) != 0) {
           Parameter param = new Parameter(nextParamIndex);
           q.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
         }
       }
     }
     String sql = q.getPlanSQL();
     q = (Query) session.prepare(sql, true);
   }
   double cost = q.getCost();
   cachedCost = new CostElement();
   cachedCost.evaluatedAt = System.currentTimeMillis();
   cachedCost.cost = cost;
   costCache.put(masksArray, cachedCost);
   return cost;
 }
Example #5
0
 private void writeValue(Value v) throws IOException {
   if (v.getType() == Value.CLOB || v.getType() == Value.BLOB) {
     if (v instanceof ValueLobDb) {
       ValueLobDb lob = (ValueLobDb) v;
       if (lob.isStored()) {
         long id = lob.getLobId();
         lobs.put(id, new CachedInputStream(null));
       }
     }
   }
   transfer.writeValue(v);
 }
Example #6
0
 /**
  * Create a new trace system object.
  *
  * @param fileName the file name
  * @param init if the trace system should be initialized
  */
 public TraceSystem(String fileName, boolean init) {
   this.fileName = fileName;
   updateLevel();
   traces = SmallLRUCache.newInstance(100);
   dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss ");
   if (fileName != null && init) {
     try {
       openWriter();
     } catch (Exception e) {
       logWritingError(e);
     }
   }
 }
Example #7
0
/** One server thread is opened per client connection. */
public class TcpServerThread implements Runnable {

  protected final Transfer transfer;
  private final TcpServer server;
  private Session session;
  private boolean stop;
  private Thread thread;
  private Command commit;
  private final SmallMap cache = new SmallMap(SysProperties.SERVER_CACHED_OBJECTS); // 默认缓存64个对象
  private final SmallLRUCache<Long, CachedInputStream> lobs =
      SmallLRUCache.newInstance(
          Math.max(
              SysProperties.SERVER_CACHED_OBJECTS, SysProperties.SERVER_RESULT_SET_FETCH_SIZE * 5));
  private final int threadId;
  private int clientVersion;
  private String sessionId;

  TcpServerThread(Socket socket, TcpServer server, int id) {
    this.server = server;
    this.threadId = id;
    transfer = new Transfer(null);
    transfer.setSocket(socket);
  }

  private void trace(String s) {
    server.trace(this + " " + s);
  }

  @Override
  public void run() {
    try {
      transfer.init();
      trace("Connect");
      // TODO server: should support a list of allowed databases
      // and a list of allowed clients
      try {
        // 如果没有加-tcpAllowOthers参数,那么只接受本地连接
        if (!server.allow(transfer.getSocket())) {
          throw DbException.get(ErrorCode.REMOTE_CONNECTION_NOT_ALLOWED);
        }
        int minClientVersion = transfer.readInt();
        if (minClientVersion < Constants.TCP_PROTOCOL_VERSION_6) {
          throw DbException.get(
              ErrorCode.DRIVER_VERSION_ERROR_2,
              "" + clientVersion,
              "" + Constants.TCP_PROTOCOL_VERSION_6);
        } else if (minClientVersion > Constants.TCP_PROTOCOL_VERSION_12) {
          throw DbException.get(
              ErrorCode.DRIVER_VERSION_ERROR_2,
              "" + clientVersion,
              "" + Constants.TCP_PROTOCOL_VERSION_12);
        }
        int maxClientVersion = transfer.readInt();
        if (maxClientVersion >= Constants.TCP_PROTOCOL_VERSION_12) {
          clientVersion = Constants.TCP_PROTOCOL_VERSION_12;
        } else {
          clientVersion = minClientVersion;
        }
        transfer.setVersion(clientVersion);
        String db = transfer.readString();
        String originalURL = transfer.readString();
        if (db == null && originalURL == null) {
          String targetSessionId = transfer.readString();
          int command = transfer.readInt();
          stop = true;
          if (command == SessionRemote.SESSION_CANCEL_STATEMENT) {
            // cancel a running statement
            int statementId = transfer.readInt();
            server.cancelStatement(targetSessionId, statementId);
          } else if (command == SessionRemote.SESSION_CHECK_KEY) {
            // check if this is the correct server
            db = server.checkKeyAndGetDatabaseName(targetSessionId);
            if (!targetSessionId.equals(db)) {
              transfer.writeInt(SessionRemote.STATUS_OK);
            } else {
              transfer.writeInt(SessionRemote.STATUS_ERROR);
            }
          }
        }
        // 启动TcpServer时加"-baseDir"或者像这样System.setProperty("h2.baseDir", "E:\\H2\\baseDir")
        String baseDir = server.getBaseDir();
        if (baseDir == null) {
          baseDir = SysProperties.getBaseDir();
        }
        // 例如启动TcpServer时,指定了"-key mydb mydatabase",
        // 如果db变量是mydb,那么实际上就是mydatabase,相当于做一次映射
        // 如果db变量不是mydb,那么抛错: org.h2.jdbc.JdbcSQLException: Wrong user name or password [28000-170]
        db = server.checkKeyAndGetDatabaseName(db);
        ConnectionInfo ci = new ConnectionInfo(db);

        ci.setOriginalURL(originalURL);
        ci.setUserName(transfer.readString());
        // password参数的值已经转换成userPasswordHash和filePasswordHash了,
        // 不能由userPasswordHash和filePasswordHash得到原始的password
        ci.setUserPasswordHash(transfer.readBytes());
        ci.setFilePasswordHash(transfer.readBytes()); // 只有指定"CIPHER"参数时filePasswordHash才是非null的
        int len = transfer.readInt();
        for (int i = 0; i < len; i++) {
          ci.setProperty(transfer.readString(), transfer.readString());
        }
        // override client's requested properties with server settings
        if (baseDir != null) {
          ci.setBaseDir(baseDir);
        }
        if (server.getIfExists()) {
          // 启动TcpServer时加"-ifExists",限制只有数据库存在时客户端才能连接,也就是不允许在客户端创建数据库
          ci.setProperty("IFEXISTS", "TRUE");
        }
        session = Engine.getInstance().createSession(ci);
        transfer.setSession(session);
        transfer.writeInt(SessionRemote.STATUS_OK);
        transfer.writeInt(clientVersion);
        transfer.flush();
        // 每建立一个新的Session对象时,把它保存到内存数据库management_db_9092的SESSIONS表
        server.addConnection(threadId, originalURL, ci.getUserName());
        trace("Connected");
      } catch (Throwable e) {
        sendError(e);
        stop = true;
      }
      while (!stop) {
        try {
          process();
        } catch (Throwable e) {
          sendError(e);
        }
      }
      trace("Disconnect");
    } catch (Throwable e) {
      server.traceError(e);
    } finally {
      close();
    }
  }

  private void closeSession() {
    if (session != null) {
      RuntimeException closeError = null;
      try {
        Command rollback = session.prepareLocal("ROLLBACK");
        rollback.executeUpdate();
      } catch (RuntimeException e) {
        closeError = e;
        server.traceError(e);
      } catch (Exception e) {
        server.traceError(e);
      }
      try {
        session.close();
        server.removeConnection(threadId);
      } catch (RuntimeException e) {
        if (closeError == null) {
          closeError = e;
          server.traceError(e);
        }
      } catch (Exception e) {
        server.traceError(e);
      } finally {
        session = null;
      }
      if (closeError != null) {
        throw closeError;
      }
    }
  }

  /** Close a connection. */
  void close() {
    try {
      stop = true;
      closeSession();
    } catch (Exception e) {
      server.traceError(e);
    } finally {
      transfer.close();
      trace("Close");
      server.remove(this);
    }
  }

  private void sendError(Throwable t) {
    try {
      SQLException e = DbException.convert(t).getSQLException();
      StringWriter writer = new StringWriter();
      e.printStackTrace(new PrintWriter(writer));
      String trace = writer.toString();
      String message;
      String sql;
      if (e instanceof JdbcSQLException) {
        JdbcSQLException j = (JdbcSQLException) e;
        message = j.getOriginalMessage();
        sql = j.getSQL();
      } else {
        message = e.getMessage();
        sql = null;
      }
      transfer
          .writeInt(SessionRemote.STATUS_ERROR)
          .writeString(e.getSQLState())
          .writeString(message)
          .writeString(sql)
          .writeInt(e.getErrorCode())
          .writeString(trace)
          .flush();
    } catch (Exception e2) {
      if (!transfer.isClosed()) {
        server.traceError(e2);
      }
      // if writing the error does not work, close the connection
      stop = true;
    }
  }

  private void setParameters(Command command) throws IOException {
    int len = transfer.readInt();
    ArrayList<? extends ParameterInterface> params = command.getParameters();
    for (int i = 0; i < len; i++) {
      Parameter p = (Parameter) params.get(i);
      p.setValue(transfer.readValue());
    }
  }

  // 总共18条命令,这里包含16条
  // 下面这两条在run方法中特殊处理
  // SESSION_CANCEL_STATEMENT
  // SESSION_CHECK_KEY
  // 分三种级别,从大到小: SESSION级、COMMAND级、RESULT级
  private void process() throws IOException {
    int operation = transfer.readInt();
    switch (operation) {
      case SessionRemote.SESSION_PREPARE_READ_PARAMS:
      case SessionRemote.SESSION_PREPARE:
        {
          int id = transfer.readInt();
          String sql = transfer.readString();
          int old = session.getModificationId();
          Command command = session.prepareLocal(sql);
          boolean readonly = command.isReadOnly();
          cache.addObject(id, command);
          boolean isQuery = command.isQuery();
          ArrayList<? extends ParameterInterface> params = command.getParameters();
          transfer
              .writeInt(getState(old))
              .writeBoolean(isQuery)
              .writeBoolean(readonly)
              .writeInt(params.size());
          if (operation == SessionRemote.SESSION_PREPARE_READ_PARAMS) {
            for (ParameterInterface p : params) {
              ParameterRemote.writeMetaData(transfer, p);
            }
          }
          transfer.flush();
          break;
        }
      case SessionRemote.SESSION_CLOSE:
        {
          stop = true;
          closeSession();
          transfer.writeInt(SessionRemote.STATUS_OK).flush();
          close();
          break;
        }
      case SessionRemote.COMMAND_COMMIT:
        {
          // 并不是通过org.h2.jdbc.JdbcConnection.commit()触发,此方法是通过发送COMMIT SQL触发
          // 触发SessionRemote.COMMAND_COMMIT的是在集群环境下,通过org.h2.engine.SessionRemote.autoCommitIfCluster()触发
          if (commit == null) {
            commit = session.prepareLocal("COMMIT");
          }
          int old = session.getModificationId();
          commit.executeUpdate();
          transfer.writeInt(getState(old)).flush();
          break;
        }
      case SessionRemote.COMMAND_GET_META_DATA:
        {
          int id = transfer.readInt();
          int objectId = transfer.readInt();
          Command command = (Command) cache.getObject(id, false);
          ResultInterface result = command.getMetaData();
          cache.addObject(objectId, result);
          int columnCount = result.getVisibleColumnCount();
          transfer.writeInt(SessionRemote.STATUS_OK).writeInt(columnCount).writeInt(0);
          for (int i = 0; i < columnCount; i++) {
            ResultColumn.writeColumn(transfer, result, i);
          }
          transfer.flush();
          break;
        }
      case SessionRemote.COMMAND_EXECUTE_QUERY:
        {
          int id = transfer.readInt();
          int objectId = transfer.readInt();
          int maxRows = transfer.readInt();
          int fetchSize = transfer.readInt();
          Command command = (Command) cache.getObject(id, false);
          setParameters(command);
          int old = session.getModificationId();
          ResultInterface result;
          synchronized (session) {
            result = command.executeQuery(maxRows, false);
          }
          cache.addObject(objectId, result);
          int columnCount = result.getVisibleColumnCount();
          int state = getState(old);
          transfer.writeInt(state).writeInt(columnCount);
          int rowCount = result.getRowCount();
          transfer.writeInt(rowCount);
          for (int i = 0; i < columnCount; i++) {
            ResultColumn.writeColumn(transfer, result, i);
          }
          int fetch = Math.min(rowCount, fetchSize);
          for (int i = 0; i < fetch; i++) {
            sendRow(result);
          }
          transfer.flush();
          break;
        }
      case SessionRemote.COMMAND_EXECUTE_UPDATE:
        {
          int id = transfer.readInt();
          Command command = (Command) cache.getObject(id, false);
          // if(command!=null)throw new Error();
          setParameters(command);
          int old = session.getModificationId();
          int updateCount;
          synchronized (session) {
            updateCount = command.executeUpdate();
          }
          int status;
          if (session.isClosed()) {
            status = SessionRemote.STATUS_CLOSED;
          } else {
            status = getState(old);
          }
          transfer.writeInt(status).writeInt(updateCount).writeBoolean(session.getAutoCommit());
          transfer.flush();
          break;
        }
      case SessionRemote.COMMAND_CLOSE:
        {
          int id = transfer.readInt();
          Command command = (Command) cache.getObject(id, true);
          if (command != null) {
            command.close();
            cache.freeObject(id);
          }
          break;
        }
      case SessionRemote.RESULT_FETCH_ROWS:
        {
          int id = transfer.readInt();
          int count = transfer.readInt();
          ResultInterface result = (ResultInterface) cache.getObject(id, false);
          transfer.writeInt(SessionRemote.STATUS_OK);
          for (int i = 0; i < count; i++) {
            sendRow(result);
          }
          transfer.flush();
          break;
        }
      case SessionRemote.RESULT_RESET:
        {
          int id = transfer.readInt();
          ResultInterface result = (ResultInterface) cache.getObject(id, false);
          result.reset();
          break;
        }
      case SessionRemote.RESULT_CLOSE:
        {
          int id = transfer.readInt();
          ResultInterface result = (ResultInterface) cache.getObject(id, true);
          if (result != null) {
            result.close();
            cache.freeObject(id);
          }
          break;
        }
      case SessionRemote.CHANGE_ID:
        {
          int oldId = transfer.readInt();
          int newId = transfer.readInt();
          Object obj = cache.getObject(oldId, false);
          cache.freeObject(oldId);
          cache.addObject(newId, obj);
          break;
        }
      case SessionRemote.SESSION_SET_ID:
        {
          sessionId = transfer.readString();
          transfer.writeInt(SessionRemote.STATUS_OK).flush();
          break;
        }
      case SessionRemote.SESSION_SET_AUTOCOMMIT:
        {
          boolean autoCommit = transfer.readBoolean();
          session.setAutoCommit(autoCommit);
          transfer.writeInt(SessionRemote.STATUS_OK).flush();
          break;
        }
      case SessionRemote.SESSION_HAS_PENDING_TRANSACTION:
        {
          transfer
              .writeInt(SessionRemote.STATUS_OK)
              .writeInt(session.hasPendingTransaction() ? 1 : 0)
              .flush();
          break;
        }
      case SessionRemote.LOB_READ:
        {
          long lobId = transfer.readLong();
          byte[] hmac;
          CachedInputStream in;
          boolean verifyMac;
          if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_11) {
            if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_12) {
              hmac = transfer.readBytes();
              verifyMac = true;
            } else {
              hmac = null;
              verifyMac = false;
            }
            in = lobs.get(lobId);
            if (in == null && verifyMac) {
              in = new CachedInputStream(null);
              lobs.put(lobId, in);
            }
          } else {
            verifyMac = false;
            hmac = null;
            in = lobs.get(lobId);
          }
          long offset = transfer.readLong();
          int length = transfer.readInt();
          if (verifyMac) {
            transfer.verifyLobMac(hmac, lobId);
          }
          if (in == null) {
            throw DbException.get(ErrorCode.OBJECT_CLOSED);
          }
          if (in.getPos() != offset) {
            LobStorageInterface lobStorage = session.getDataHandler().getLobStorage();
            InputStream lobIn = lobStorage.getInputStream(lobId, hmac, -1);
            in = new CachedInputStream(lobIn);
            lobs.put(lobId, in);
            lobIn.skip(offset);
          }
          // limit the buffer size
          length = Math.min(16 * Constants.IO_BUFFER_SIZE, length);
          byte[] buff = new byte[length];
          length = IOUtils.readFully(in, buff, 0, length);
          transfer.writeInt(SessionRemote.STATUS_OK);
          transfer.writeInt(length);
          transfer.writeBytes(buff, 0, length);
          transfer.flush();
          break;
        }
      default:
        trace("Unknown operation: " + operation);
        closeSession();
        close();
    }
  }

  private int getState(int oldModificationId) {
    if (session.getModificationId() == oldModificationId) {
      return SessionRemote.STATUS_OK;
    }
    return SessionRemote.STATUS_OK_STATE_CHANGED;
  }

  private void sendRow(ResultInterface result) throws IOException {
    if (result.next()) {
      transfer.writeBoolean(true);
      Value[] v = result.currentRow();
      for (int i = 0; i < result.getVisibleColumnCount(); i++) {
        if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_12) {
          transfer.writeValue(v[i]);
        } else {
          writeValue(v[i]);
        }
      }
    } else {
      transfer.writeBoolean(false);
    }
  }

  private void writeValue(Value v) throws IOException {
    if (v.getType() == Value.CLOB || v.getType() == Value.BLOB) {
      if (v instanceof ValueLobDb) {
        ValueLobDb lob = (ValueLobDb) v;
        if (lob.isStored()) {
          long id = lob.getLobId();
          lobs.put(id, new CachedInputStream(null));
        }
      }
    }
    transfer.writeValue(v);
  }

  void setThread(Thread thread) {
    this.thread = thread;
  }

  Thread getThread() {
    return thread;
  }

  /**
   * Cancel a running statement.
   *
   * @param targetSessionId the session id
   * @param statementId the statement to cancel
   */
  void cancelStatement(String targetSessionId, int statementId) {
    if (StringUtils.equals(targetSessionId, this.sessionId)) {
      Command cmd = (Command) cache.getObject(statementId, false);
      cmd.cancel();
    }
  }

  /** An input stream with a position. */
  static class CachedInputStream extends FilterInputStream {

    private static final ByteArrayInputStream DUMMY = new ByteArrayInputStream(new byte[0]);
    private long pos;

    CachedInputStream(InputStream in) {
      super(in == null ? DUMMY : in);
      if (in == null) {
        pos = -1;
      }
    }

    @Override
    public int read(byte[] buff, int off, int len) throws IOException {
      len = super.read(buff, off, len);
      if (len > 0) {
        pos += len;
      }
      return len;
    }

    @Override
    public int read() throws IOException {
      int x = in.read();
      if (x >= 0) {
        pos++;
      }
      return x;
    }

    @Override
    public long skip(long n) throws IOException {
      n = super.skip(n);
      if (n > 0) {
        pos += n;
      }
      return n;
    }

    public long getPos() {
      return pos;
    }
  }
}
Example #8
0
 // 总共18条命令,这里包含16条
 // 下面这两条在run方法中特殊处理
 // SESSION_CANCEL_STATEMENT
 // SESSION_CHECK_KEY
 // 分三种级别,从大到小: SESSION级、COMMAND级、RESULT级
 private void process() throws IOException {
   int operation = transfer.readInt();
   switch (operation) {
     case SessionRemote.SESSION_PREPARE_READ_PARAMS:
     case SessionRemote.SESSION_PREPARE:
       {
         int id = transfer.readInt();
         String sql = transfer.readString();
         int old = session.getModificationId();
         Command command = session.prepareLocal(sql);
         boolean readonly = command.isReadOnly();
         cache.addObject(id, command);
         boolean isQuery = command.isQuery();
         ArrayList<? extends ParameterInterface> params = command.getParameters();
         transfer
             .writeInt(getState(old))
             .writeBoolean(isQuery)
             .writeBoolean(readonly)
             .writeInt(params.size());
         if (operation == SessionRemote.SESSION_PREPARE_READ_PARAMS) {
           for (ParameterInterface p : params) {
             ParameterRemote.writeMetaData(transfer, p);
           }
         }
         transfer.flush();
         break;
       }
     case SessionRemote.SESSION_CLOSE:
       {
         stop = true;
         closeSession();
         transfer.writeInt(SessionRemote.STATUS_OK).flush();
         close();
         break;
       }
     case SessionRemote.COMMAND_COMMIT:
       {
         // 并不是通过org.h2.jdbc.JdbcConnection.commit()触发,此方法是通过发送COMMIT SQL触发
         // 触发SessionRemote.COMMAND_COMMIT的是在集群环境下,通过org.h2.engine.SessionRemote.autoCommitIfCluster()触发
         if (commit == null) {
           commit = session.prepareLocal("COMMIT");
         }
         int old = session.getModificationId();
         commit.executeUpdate();
         transfer.writeInt(getState(old)).flush();
         break;
       }
     case SessionRemote.COMMAND_GET_META_DATA:
       {
         int id = transfer.readInt();
         int objectId = transfer.readInt();
         Command command = (Command) cache.getObject(id, false);
         ResultInterface result = command.getMetaData();
         cache.addObject(objectId, result);
         int columnCount = result.getVisibleColumnCount();
         transfer.writeInt(SessionRemote.STATUS_OK).writeInt(columnCount).writeInt(0);
         for (int i = 0; i < columnCount; i++) {
           ResultColumn.writeColumn(transfer, result, i);
         }
         transfer.flush();
         break;
       }
     case SessionRemote.COMMAND_EXECUTE_QUERY:
       {
         int id = transfer.readInt();
         int objectId = transfer.readInt();
         int maxRows = transfer.readInt();
         int fetchSize = transfer.readInt();
         Command command = (Command) cache.getObject(id, false);
         setParameters(command);
         int old = session.getModificationId();
         ResultInterface result;
         synchronized (session) {
           result = command.executeQuery(maxRows, false);
         }
         cache.addObject(objectId, result);
         int columnCount = result.getVisibleColumnCount();
         int state = getState(old);
         transfer.writeInt(state).writeInt(columnCount);
         int rowCount = result.getRowCount();
         transfer.writeInt(rowCount);
         for (int i = 0; i < columnCount; i++) {
           ResultColumn.writeColumn(transfer, result, i);
         }
         int fetch = Math.min(rowCount, fetchSize);
         for (int i = 0; i < fetch; i++) {
           sendRow(result);
         }
         transfer.flush();
         break;
       }
     case SessionRemote.COMMAND_EXECUTE_UPDATE:
       {
         int id = transfer.readInt();
         Command command = (Command) cache.getObject(id, false);
         // if(command!=null)throw new Error();
         setParameters(command);
         int old = session.getModificationId();
         int updateCount;
         synchronized (session) {
           updateCount = command.executeUpdate();
         }
         int status;
         if (session.isClosed()) {
           status = SessionRemote.STATUS_CLOSED;
         } else {
           status = getState(old);
         }
         transfer.writeInt(status).writeInt(updateCount).writeBoolean(session.getAutoCommit());
         transfer.flush();
         break;
       }
     case SessionRemote.COMMAND_CLOSE:
       {
         int id = transfer.readInt();
         Command command = (Command) cache.getObject(id, true);
         if (command != null) {
           command.close();
           cache.freeObject(id);
         }
         break;
       }
     case SessionRemote.RESULT_FETCH_ROWS:
       {
         int id = transfer.readInt();
         int count = transfer.readInt();
         ResultInterface result = (ResultInterface) cache.getObject(id, false);
         transfer.writeInt(SessionRemote.STATUS_OK);
         for (int i = 0; i < count; i++) {
           sendRow(result);
         }
         transfer.flush();
         break;
       }
     case SessionRemote.RESULT_RESET:
       {
         int id = transfer.readInt();
         ResultInterface result = (ResultInterface) cache.getObject(id, false);
         result.reset();
         break;
       }
     case SessionRemote.RESULT_CLOSE:
       {
         int id = transfer.readInt();
         ResultInterface result = (ResultInterface) cache.getObject(id, true);
         if (result != null) {
           result.close();
           cache.freeObject(id);
         }
         break;
       }
     case SessionRemote.CHANGE_ID:
       {
         int oldId = transfer.readInt();
         int newId = transfer.readInt();
         Object obj = cache.getObject(oldId, false);
         cache.freeObject(oldId);
         cache.addObject(newId, obj);
         break;
       }
     case SessionRemote.SESSION_SET_ID:
       {
         sessionId = transfer.readString();
         transfer.writeInt(SessionRemote.STATUS_OK).flush();
         break;
       }
     case SessionRemote.SESSION_SET_AUTOCOMMIT:
       {
         boolean autoCommit = transfer.readBoolean();
         session.setAutoCommit(autoCommit);
         transfer.writeInt(SessionRemote.STATUS_OK).flush();
         break;
       }
     case SessionRemote.SESSION_HAS_PENDING_TRANSACTION:
       {
         transfer
             .writeInt(SessionRemote.STATUS_OK)
             .writeInt(session.hasPendingTransaction() ? 1 : 0)
             .flush();
         break;
       }
     case SessionRemote.LOB_READ:
       {
         long lobId = transfer.readLong();
         byte[] hmac;
         CachedInputStream in;
         boolean verifyMac;
         if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_11) {
           if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_12) {
             hmac = transfer.readBytes();
             verifyMac = true;
           } else {
             hmac = null;
             verifyMac = false;
           }
           in = lobs.get(lobId);
           if (in == null && verifyMac) {
             in = new CachedInputStream(null);
             lobs.put(lobId, in);
           }
         } else {
           verifyMac = false;
           hmac = null;
           in = lobs.get(lobId);
         }
         long offset = transfer.readLong();
         int length = transfer.readInt();
         if (verifyMac) {
           transfer.verifyLobMac(hmac, lobId);
         }
         if (in == null) {
           throw DbException.get(ErrorCode.OBJECT_CLOSED);
         }
         if (in.getPos() != offset) {
           LobStorageInterface lobStorage = session.getDataHandler().getLobStorage();
           InputStream lobIn = lobStorage.getInputStream(lobId, hmac, -1);
           in = new CachedInputStream(lobIn);
           lobs.put(lobId, in);
           lobIn.skip(offset);
         }
         // limit the buffer size
         length = Math.min(16 * Constants.IO_BUFFER_SIZE, length);
         byte[] buff = new byte[length];
         length = IOUtils.readFully(in, buff, 0, length);
         transfer.writeInt(SessionRemote.STATUS_OK);
         transfer.writeInt(length);
         transfer.writeBytes(buff, 0, length);
         transfer.flush();
         break;
       }
     default:
       trace("Unknown operation: " + operation);
       closeSession();
       close();
   }
 }
Example #9
0
/** A view is a virtual table that is defined by a query. */
public class TableView extends Table {

  private static final long ROW_COUNT_APPROXIMATION = 100;

  private String querySQL;
  private ArrayList<Table> tables;
  private final String[] columnNames;
  private Query viewQuery;
  private ViewIndex index;
  private boolean recursive;
  private DbException createException;
  private SmallLRUCache<IntArray, ViewIndex> indexCache =
      SmallLRUCache.newInstance(Constants.VIEW_INDEX_CACHE_SIZE);
  private long lastModificationCheck;
  private long maxDataModificationId;
  private User owner;
  private Query topQuery;
  private ResultInterface recursiveResult;
  private boolean tableExpression;

  public TableView(
      Schema schema,
      int id,
      String name,
      String querySQL,
      ArrayList<Parameter> params,
      String[] columnNames,
      Session session,
      boolean recursive) {
    super(schema, id, name, false, true);
    this.querySQL = querySQL;
    this.columnNames = columnNames;
    this.recursive = recursive;
    index = new ViewIndex(this, querySQL, params, recursive);
    initColumnsAndTables(session);
  }

  /**
   * Re-compile the query, updating the SQL statement.
   *
   * @param session the session
   * @return the query
   */
  public Query recompileQuery(Session session) {
    Prepared p = session.prepare(querySQL);
    if (!(p instanceof Query)) {
      throw DbException.getSyntaxError(querySQL, 0);
    }
    Query query = (Query) p;
    querySQL = query.getPlanSQL();
    return query;
  }

  private void initColumnsAndTables(Session session) {
    Column[] cols;
    removeViewFromTables();
    try {
      Query query = recompileQuery(session);
      tables = New.arrayList(query.getTables());
      ArrayList<Expression> expressions = query.getExpressions();
      ArrayList<Column> list = New.arrayList();
      for (int i = 0; i < query.getColumnCount(); i++) {
        Expression expr = expressions.get(i);
        String name = null;
        if (columnNames != null && columnNames.length > i) {
          name = columnNames[i];
        }
        if (name == null) {
          name = expr.getAlias();
        }
        int type = expr.getType();
        long precision = expr.getPrecision();
        int scale = expr.getScale();
        int displaySize = expr.getDisplaySize();
        Column col = new Column(name, type, precision, scale, displaySize);
        col.setTable(this, i);
        list.add(col);
      }
      cols = new Column[list.size()];
      list.toArray(cols);
      createException = null;
      viewQuery = query;
    } catch (DbException e) {
      createException = e;
      // if it can't be compiled, then it's a 'zero column table'
      // this avoids problems when creating the view when opening the
      // database
      tables = New.arrayList();
      cols = new Column[0];
      if (recursive && columnNames != null) {
        cols = new Column[columnNames.length];
        for (int i = 0; i < columnNames.length; i++) {
          cols[i] = new Column(columnNames[i], Value.STRING);
        }
        index.setRecursive(true);
        createException = null;
      }
    }
    setColumns(cols);
    if (getId() != 0) {
      addViewToTables();
    }
  }

  /**
   * Check if this view is currently invalid.
   *
   * @return true if it is
   */
  public boolean isInvalid() {
    return createException != null;
  }

  public PlanItem getBestPlanItem(Session session, int[] masks) {
    PlanItem item = new PlanItem();
    item.cost = index.getCost(session, masks);
    IntArray masksArray = new IntArray(masks == null ? Utils.EMPTY_INT_ARRAY : masks);
    ViewIndex i2 = indexCache.get(masksArray);
    if (i2 == null || i2.getSession() != session) {
      i2 = new ViewIndex(this, index, session, masks);
      indexCache.put(masksArray, i2);
    }
    item.setIndex(i2);
    return item;
  }

  public String getDropSQL() {
    StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS ");
    buff.append(getSQL());
    return buff.toString();
  }

  public String getCreateSQL() {
    return getCreateSQL(false, true);
  }

  /**
   * Generate "CREATE" SQL statement for the view.
   *
   * @param orReplace if true, then include the OR REPLACE clause
   * @param force if true, then include the FORCE clause
   * @return the SQL statement
   */
  public String getCreateSQL(boolean orReplace, boolean force) {
    StatementBuilder buff = new StatementBuilder("CREATE ");
    if (orReplace) {
      buff.append("OR REPLACE ");
    }
    if (force) {
      buff.append("FORCE ");
    }
    buff.append("VIEW ");
    buff.append(getSQL());
    if (comment != null) {
      buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment));
    }
    if (columns.length > 0) {
      buff.append('(');
      for (Column c : columns) {
        buff.appendExceptFirst(", ");
        buff.append(c.getSQL());
      }
      buff.append(')');
    } else if (columnNames != null) {
      buff.append('(');
      for (String n : columnNames) {
        buff.appendExceptFirst(", ");
        buff.append(n);
      }
      buff.append(')');
    }
    return buff.append(" AS\n").append(querySQL).toString();
  }

  public void checkRename() {
    // ok
  }

  public void lock(Session session, boolean exclusive, boolean force) {
    // exclusive lock means: the view will be dropped
  }

  public void close(Session session) {
    // nothing to do
  }

  public void unlock(Session s) {
    // nothing to do
  }

  public boolean isLockedExclusively() {
    return false;
  }

  public Index addIndex(
      Session session,
      String indexName,
      int indexId,
      IndexColumn[] cols,
      IndexType indexType,
      boolean create,
      String indexComment) {
    throw DbException.getUnsupportedException("VIEW");
  }

  public void removeRow(Session session, Row row) {
    throw DbException.getUnsupportedException("VIEW");
  }

  public void addRow(Session session, Row row) {
    throw DbException.getUnsupportedException("VIEW");
  }

  public void checkSupportAlter() {
    throw DbException.getUnsupportedException("VIEW");
  }

  public void truncate(Session session) {
    throw DbException.getUnsupportedException("VIEW");
  }

  public long getRowCount(Session session) {
    throw DbException.throwInternalError();
  }

  public boolean canGetRowCount() {
    // TODO view: could get the row count, but not that easy
    return false;
  }

  public boolean canDrop() {
    return true;
  }

  public String getTableType() {
    return Table.VIEW;
  }

  public void removeChildrenAndResources(Session session) {
    removeViewFromTables();
    super.removeChildrenAndResources(session);
    database.removeMeta(session, getId());
    querySQL = null;
    index = null;
    invalidate();
  }

  public String getSQL() {
    if (isTemporary()) {
      return "(" + querySQL + ")";
    }
    return super.getSQL();
  }

  public String getQuery() {
    return querySQL;
  }

  public Index getScanIndex(Session session) {
    if (createException != null) {
      String msg = createException.getMessage();
      throw DbException.get(ErrorCode.VIEW_IS_INVALID_2, createException, getSQL(), msg);
    }
    PlanItem item = getBestPlanItem(session, null);
    return item.getIndex();
  }

  public ArrayList<Index> getIndexes() {
    return null;
  }

  /**
   * Re-compile the view query.
   *
   * @param session the session
   */
  public void recompile(Session session) {
    for (Table t : tables) {
      t.removeView(this);
    }
    tables.clear();
    initColumnsAndTables(session);
  }

  public long getMaxDataModificationId() {
    if (createException != null) {
      return Long.MAX_VALUE;
    }
    if (viewQuery == null) {
      return Long.MAX_VALUE;
    }
    // if nothing was modified in the database since the last check, and the
    // last is known, then we don't need to check again
    // this speeds up nested views
    long dbMod = database.getModificationDataId();
    if (dbMod > lastModificationCheck && maxDataModificationId <= dbMod) {
      maxDataModificationId = viewQuery.getMaxDataModificationId();
      lastModificationCheck = dbMod;
    }
    return maxDataModificationId;
  }

  public Index getUniqueIndex() {
    return null;
  }

  private void removeViewFromTables() {
    if (tables != null) {
      for (Table t : tables) {
        t.removeView(this);
      }
      tables.clear();
    }
  }

  private void addViewToTables() {
    for (Table t : tables) {
      t.addView(this);
    }
  }

  private void setOwner(User owner) {
    this.owner = owner;
  }

  public User getOwner() {
    return owner;
  }

  /**
   * Create a temporary view out of the given query.
   *
   * @param session the session
   * @param owner the owner of the query
   * @param name the view name
   * @param query the query
   * @param topQuery the top level query
   * @return the view table
   */
  public static TableView createTempView(
      Session session, User owner, String name, Query query, Query topQuery) {
    Schema mainSchema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN);
    String querySQL = query.getPlanSQL();
    TableView v =
        new TableView(mainSchema, 0, name, querySQL, query.getParameters(), null, session, false);
    v.setTopQuery(topQuery);
    if (v.createException != null) {
      throw v.createException;
    }
    v.setOwner(owner);
    v.setTemporary(true);
    return v;
  }

  private void setTopQuery(Query topQuery) {
    this.topQuery = topQuery;
  }

  public long getRowCountApproximation() {
    return ROW_COUNT_APPROXIMATION;
  }

  public int getParameterOffset() {
    return topQuery == null ? 0 : topQuery.getParameters().size();
  }

  public boolean isDeterministic() {
    if (recursive) {
      return false;
    }
    return viewQuery.isEverything(ExpressionVisitor.DETERMINISTIC);
  }

  public void setRecursiveResult(ResultInterface recursiveResult) {
    this.recursiveResult = recursiveResult;
  }

  public ResultInterface getRecursiveResult() {
    return recursiveResult;
  }

  public void setTableExpression(boolean tableExpression) {
    this.tableExpression = tableExpression;
  }

  public boolean isTableExpression() {
    return tableExpression;
  }
}
Example #10
0
/**
 * This object represents a virtual index for a query. Actually it only represents a prepared SELECT
 * statement.
 */
public class ViewIndex extends BaseIndex {

  private final TableView view;
  private final String querySQL;
  private final ArrayList<Parameter> originalParameters;
  private final SmallLRUCache<IntArray, CostElement> costCache =
      SmallLRUCache.newInstance(Constants.VIEW_INDEX_CACHE_SIZE);
  private boolean recursive;
  private int[] indexMasks;
  private String planSQL;
  private Query query;
  private Session createSession;

  public ViewIndex(
      TableView view, String querySQL, ArrayList<Parameter> originalParameters, boolean recursive) {
    initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
    this.view = view;
    this.querySQL = querySQL;
    this.originalParameters = originalParameters;
    this.recursive = recursive;
    columns = new Column[0];
  }

  public ViewIndex(TableView view, ViewIndex index, Session session, int[] masks) {
    initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
    this.view = view;
    this.querySQL = index.querySQL;
    this.originalParameters = index.originalParameters;
    this.recursive = index.recursive;
    this.indexMasks = masks;
    this.createSession = session;
    columns = new Column[0];
    if (!recursive) {
      query = getQuery(session, masks);
      planSQL = query.getPlanSQL();
    }
  }

  public Session getSession() {
    return createSession;
  }

  public String getPlanSQL() {
    return planSQL;
  }

  public void close(Session session) {
    // nothing to do
  }

  public void add(Session session, Row row) {
    throw DbException.getUnsupportedException("VIEW");
  }

  public void remove(Session session, Row row) {
    throw DbException.getUnsupportedException("VIEW");
  }

  /** A calculated cost value. */
  static class CostElement {

    /** The time in milliseconds when this cost was calculated. */
    long evaluatedAt;

    /** The cost. */
    double cost;
  }

  public double getCost(Session session, int[] masks) {
    if (recursive) {
      return 1000;
    }
    IntArray masksArray = new IntArray(masks == null ? Utils.EMPTY_INT_ARRAY : masks);
    CostElement cachedCost = costCache.get(masksArray);
    if (cachedCost != null) {
      long time = System.currentTimeMillis();
      if (time < cachedCost.evaluatedAt + Constants.VIEW_COST_CACHE_MAX_AGE) {
        return cachedCost.cost;
      }
    }
    Query q = (Query) session.prepare(querySQL, true);
    if (masks != null) {
      IntArray paramIndex = new IntArray();
      for (int i = 0; i < masks.length; i++) {
        int mask = masks[i];
        if (mask == 0) {
          continue;
        }
        paramIndex.add(i);
      }
      int len = paramIndex.size();
      for (int i = 0; i < len; i++) {
        int idx = paramIndex.get(i);
        int mask = masks[idx];
        int nextParamIndex = q.getParameters().size() + view.getParameterOffset();
        if ((mask & IndexCondition.EQUALITY) != 0) {
          Parameter param = new Parameter(nextParamIndex);
          q.addGlobalCondition(param, idx, Comparison.EQUAL_NULL_SAFE);
        } else {
          if ((mask & IndexCondition.START) != 0) {
            Parameter param = new Parameter(nextParamIndex);
            q.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
          }
          if ((mask & IndexCondition.END) != 0) {
            Parameter param = new Parameter(nextParamIndex);
            q.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
          }
        }
      }
      String sql = q.getPlanSQL();
      q = (Query) session.prepare(sql, true);
    }
    double cost = q.getCost();
    cachedCost = new CostElement();
    cachedCost.evaluatedAt = System.currentTimeMillis();
    cachedCost.cost = cost;
    costCache.put(masksArray, cachedCost);
    return cost;
  }

  public Cursor find(Session session, SearchRow first, SearchRow last) {
    if (recursive) {
      if (view.getRecursiveResult() != null) {
        ResultInterface r = view.getRecursiveResult();
        r.reset();
        return new ViewCursor(table, r);
      }
      if (query == null) {
        query = (Query) createSession.prepare(querySQL, true);
        planSQL = query.getPlanSQL();
      }
      if (!(query instanceof SelectUnion)) {
        throw DbException.get(ErrorCode.SYNTAX_ERROR_2, "recursive queries without UNION ALL");
      }
      SelectUnion union = (SelectUnion) query;
      if (union.getUnionType() != SelectUnion.UNION_ALL) {
        throw DbException.get(ErrorCode.SYNTAX_ERROR_2, "recursive queries without UNION ALL");
      }
      Query left = union.getLeft();
      ResultInterface r = left.query(0);
      LocalResult result = union.getEmptyResult();
      while (r.next()) {
        result.addRow(r.currentRow());
      }
      Query right = union.getRight();
      r.reset();
      view.setRecursiveResult(r);
      while (true) {
        r = right.query(0);
        if (r.getRowCount() == 0) {
          break;
        }
        while (r.next()) {
          result.addRow(r.currentRow());
        }
        r.reset();
        view.setRecursiveResult(r);
      }
      return new ViewCursor(table, result);
    }
    ArrayList<Parameter> paramList = query.getParameters();
    for (int i = 0; originalParameters != null && i < originalParameters.size(); i++) {
      Parameter orig = originalParameters.get(i);
      int idx = orig.getIndex();
      Value value = orig.getValue(session);
      setParameter(paramList, idx, value);
    }
    int len;
    if (first != null) {
      len = first.getColumnCount();
    } else if (last != null) {
      len = last.getColumnCount();
    } else {
      len = 0;
    }
    int idx = originalParameters == null ? 0 : originalParameters.size();
    idx += view.getParameterOffset();
    for (int i = 0; i < len; i++) {
      if (first != null) {
        Value v = first.getValue(i);
        if (v != null) {
          int x = idx++;
          setParameter(paramList, x, v);
        }
      }
      // for equality, only one parameter is used (first == last)
      if (last != null && indexMasks[i] != IndexCondition.EQUALITY) {
        Value v = last.getValue(i);
        if (v != null) {
          int x = idx++;
          setParameter(paramList, x, v);
        }
      }
    }
    ResultInterface result = query.query(0);
    return new ViewCursor(table, result);
  }

  private void setParameter(ArrayList<Parameter> paramList, int x, Value v) {
    if (x >= paramList.size()) {
      // the parameter may be optimized away as in
      // select * from (select null as x) where x=1;
      return;
    }
    Parameter param = paramList.get(x);
    param.setValue(v);
  }

  private Query getQuery(Session session, int[] masks) {
    Query q = (Query) session.prepare(querySQL, true);
    if (masks == null) {
      return q;
    }
    int firstIndexParam = originalParameters == null ? 0 : originalParameters.size();
    firstIndexParam += view.getParameterOffset();
    IntArray paramIndex = new IntArray();
    for (int i = 0; i < masks.length; i++) {
      int mask = masks[i];
      if (mask == 0) {
        continue;
      }
      paramIndex.add(i);
      if ((mask & IndexCondition.RANGE) == IndexCondition.RANGE) {
        // two parameters for range queries: >= x AND <= y
        paramIndex.add(i);
      }
    }
    int len = paramIndex.size();
    columns = new Column[len];
    for (int i = 0; i < len; ) {
      int idx = paramIndex.get(i);
      Column col = table.getColumn(idx);
      columns[i] = col;
      int mask = masks[idx];
      if ((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
        Parameter param = new Parameter(firstIndexParam + i);
        q.addGlobalCondition(param, idx, Comparison.EQUAL_NULL_SAFE);
        i++;
      } else {
        if ((mask & IndexCondition.START) == IndexCondition.START) {
          Parameter param = new Parameter(firstIndexParam + i);
          q.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
          i++;
        }
        if ((mask & IndexCondition.END) == IndexCondition.END) {
          Parameter param = new Parameter(firstIndexParam + i);
          q.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
          i++;
        }
      }
    }
    String sql = q.getPlanSQL();
    q = (Query) session.prepare(sql, true);
    return q;
  }

  public void remove(Session session) {
    throw DbException.getUnsupportedException("VIEW");
  }

  public void truncate(Session session) {
    throw DbException.getUnsupportedException("VIEW");
  }

  public void checkRename() {
    throw DbException.getUnsupportedException("VIEW");
  }

  public boolean needRebuild() {
    return false;
  }

  public boolean canGetFirstOrLast() {
    return false;
  }

  public Cursor findFirstOrLast(Session session, boolean first) {
    throw DbException.getUnsupportedException("VIEW");
  }

  public void setRecursive(boolean value) {
    this.recursive = value;
  }

  public long getRowCount(Session session) {
    return 0;
  }

  public long getRowCountApproximation() {
    return 0;
  }

  public boolean isRecursive() {
    return recursive;
  }
}