예제 #1
1
 @Override
 public Value createBlob(InputStream in, long maxLength) {
   init();
   int type = Value.BLOB;
   if (maxLength < 0) {
     maxLength = Long.MAX_VALUE;
   }
   int max = (int) Math.min(maxLength, database.getMaxLengthInplaceLob());
   try {
     if (max != 0 && max < Integer.MAX_VALUE) {
       BufferedInputStream b = new BufferedInputStream(in, max);
       b.mark(max);
       byte[] small = new byte[max];
       int len = IOUtils.readFully(b, small, max);
       if (len < max) {
         if (len < small.length) {
           small = Arrays.copyOf(small, len);
         }
         return ValueLobDb.createSmallLob(type, small);
       }
       b.reset();
       in = b;
     }
     if (maxLength != Long.MAX_VALUE) {
       in = new LimitInputStream(in, maxLength);
     }
     return createLob(in, type);
   } catch (IllegalStateException e) {
     throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
   } catch (IOException e) {
     throw DbException.convertIOException(e, null);
   }
 }
예제 #2
0
 @Override
 public void prepare() {
   if (columns == null) {
     // 如INSERT INTO InsertTest DEFAULT VALUES
     if (list.size() > 0 && list.get(0).length == 0) {
       // special case where table is used as a sequence
       columns = new Column[0];
     } else { // 如INSERT INTO InsertTest(SELECT * FROM tmpSelectTest)
       columns = table.getColumns();
     }
   }
   if (list.size() > 0) {
     for (Expression[] expr : list) {
       if (expr.length != columns.length) {
         throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
       }
       for (int i = 0, len = expr.length; i < len; i++) {
         Expression e = expr[i];
         if (e != null) {
           e = e.optimize(session);
           if (e instanceof Parameter) {
             Parameter p = (Parameter) e;
             p.setColumn(columns[i]);
           }
           expr[i] = e;
         }
       }
     }
   } else {
     query.prepare();
     if (query.getColumnCount() != columns.length) {
       throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
     }
   }
 }
예제 #3
0
 @Override
 public int update() {
   session.getUser().checkAdmin();
   session.commit(true);
   Database db = session.getDatabase();
   if (db.findRole(userName) != null) {
     throw DbException.get(ErrorCode.ROLE_ALREADY_EXISTS_1, userName);
   }
   if (db.findUser(userName) != null) {
     if (ifNotExists) {
       return 0;
     }
     throw DbException.get(ErrorCode.USER_ALREADY_EXISTS_1, userName);
   }
   int id = getObjectId();
   User user = new User(db, id, userName, false);
   user.setAdmin(admin);
   user.setComment(comment);
   if (hash != null && salt != null) {
     user.setSaltAndHash(getByteArray(salt), getByteArray(hash));
   } else if (password != null) {
     char[] passwordChars = getCharArray(password);
     byte[] userPasswordHash;
     if (userName.length() == 0 && passwordChars.length == 0) {
       userPasswordHash = new byte[0];
     } else {
       userPasswordHash = SHA256.getKeyPasswordHash(userName, passwordChars);
     }
     user.setUserPasswordHash(userPasswordHash);
   } else {
     throw DbException.throwInternalError();
   }
   db.addDatabaseObject(session, user);
   return 0;
 }
예제 #4
0
 /**
  * Undo all operations back to the log position of the given savepoint.
  *
  * @param name the savepoint name
  */
 public void rollbackToSavepoint(String name) {
   checkCommitRollback();
   if (savepoints == null) {
     throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1, name);
   }
   Savepoint savepoint = savepoints.get(name);
   if (savepoint == null) {
     throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1, name);
   }
   rollbackTo(savepoint, false);
 }
예제 #5
0
 /**
  * INTERNAL. Check if this connection is closed.
  *
  * @param write if the next operation is possibly writing
  * @throws SQLException if the connection or session is closed
  */
 protected void checkClosed(boolean write) throws SQLException {
   if (session == null) {
     throw DbException.get(ErrorCode.OBJECT_CLOSED);
   }
   if (session.isClosed()) {
     throw DbException.get(ErrorCode.DATABASE_CALLED_AT_SHUTDOWN);
   }
   if (session.isReconnectNeeded(write)) {
     trace.debug("reconnect");
     closePreparedCommands();
     session = session.reconnect(write);
     setTrace(session.getTrace());
   }
 }
예제 #6
0
 /**
  * Parse a string to a ValueTime.
  *
  * @param s the string to parse
  * @return the time
  */
 public static ValueTime parse(String s) {
   try {
     return fromNanos(DateTimeUtils.parseTimeNanos(s, 0, s.length(), false));
   } catch (Exception e) {
     throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, e, "TIME", s);
   }
 }
예제 #7
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;
 }
예제 #8
0
 @Override
 public Value getValue(Session session) {
   query.setSession(session);
   ResultInterface result = query.query(2);
   try {
     int rowcount = result.getRowCount();
     if (rowcount > 1) {
       throw DbException.get(ErrorCode.SCALAR_SUBQUERY_CONTAINS_MORE_THAN_ONE_ROW);
     }
     Value v;
     if (rowcount <= 0) {
       v = ValueNull.INSTANCE;
     } else {
       result.next();
       Value[] values = result.currentRow();
       if (result.getVisibleColumnCount() == 1) {
         v = values[0];
       } else {
         v = ValueArray.get(values);
       }
     }
     return v;
   } finally {
     result.close();
   }
 }
예제 #9
0
 /**
  * Creates a new sequence.
  *
  * @param schema the schema
  * @param id the object id
  * @param name the sequence name
  * @param startValue the first value to return
  * @param increment the increment count
  * @param cacheSize the number of entries to pre-fetch
  * @param minValue the minimum value
  * @param maxValue the maximum value
  * @param cycle whether to jump back to the min value if needed
  * @param belongsToTable whether this sequence belongs to a table (for auto-increment columns)
  */
 public Sequence(
     Schema schema,
     int id,
     String name,
     Long startValue,
     Long increment,
     Long cacheSize,
     Long minValue,
     Long maxValue,
     boolean cycle,
     boolean belongsToTable) {
   initSchemaObjectBase(schema, id, name, Trace.SEQUENCE);
   this.increment = increment != null ? increment : 1;
   this.minValue = minValue != null ? minValue : getDefaultMinValue(startValue, this.increment);
   this.maxValue = maxValue != null ? maxValue : getDefaultMaxValue(startValue, this.increment);
   this.value = startValue != null ? startValue : getDefaultStartValue(this.increment);
   this.valueWithMargin = value;
   this.cacheSize = cacheSize != null ? Math.max(1, cacheSize) : DEFAULT_CACHE_SIZE;
   this.cycle = cycle;
   this.belongsToTable = belongsToTable;
   if (!isValid(this.value, this.minValue, this.maxValue, this.increment)) {
     throw DbException.get(
         ErrorCode.SEQUENCE_ATTRIBUTES_INVALID,
         name,
         String.valueOf(this.value),
         String.valueOf(this.minValue),
         String.valueOf(this.maxValue),
         String.valueOf(this.increment));
   }
 }
예제 #10
0
 @Override
 public Index getScanIndex(Session session) {
   if (getStep(session) == 0) {
     throw DbException.get(ErrorCode.STEP_SIZE_MUST_NOT_BE_ZERO);
   }
   return new RangeIndex(this, IndexColumn.wrap(columns));
 }
예제 #11
0
 /**
  * The maximum number of rows, including uncommitted rows of any session.
  *
  * @return the maximum number of rows
  */
 public long getRowCountMax() {
   try {
     return dataMap.sizeAsLongMax();
   } catch (IllegalStateException e) {
     throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
   }
 }
예제 #12
0
 /**
  * Allows the start value, increment, min value and max value to be updated atomically, including
  * atomic validation. Useful because setting these attributes one after the other could otherwise
  * result in an invalid sequence state (e.g. min value > max value, start value < min value, etc).
  *
  * @param startValue the new start value (<code>null</code> if no change)
  * @param minValue the new min value (<code>null</code> if no change)
  * @param maxValue the new max value (<code>null</code> if no change)
  * @param increment the new increment (<code>null</code> if no change)
  */
 public synchronized void modify(Long startValue, Long minValue, Long maxValue, Long increment) {
   if (startValue == null) {
     startValue = this.value;
   }
   if (minValue == null) {
     minValue = this.minValue;
   }
   if (maxValue == null) {
     maxValue = this.maxValue;
   }
   if (increment == null) {
     increment = this.increment;
   }
   if (!isValid(startValue, minValue, maxValue, increment)) {
     throw DbException.get(
         ErrorCode.SEQUENCE_ATTRIBUTES_INVALID,
         getName(),
         String.valueOf(this.value),
         String.valueOf(this.minValue),
         String.valueOf(this.maxValue),
         String.valueOf(this.increment));
   }
   this.value = startValue;
   this.valueWithMargin = startValue;
   this.minValue = minValue;
   this.maxValue = maxValue;
   this.increment = increment;
 }
예제 #13
0
 /**
  * Commit or roll back the given transaction.
  *
  * @param transactionName the name of the transaction
  * @param commit true for commit, false for rollback
  */
 public void setPreparedTransaction(String transactionName, boolean commit) {
   if (currentTransactionName != null && currentTransactionName.equals(transactionName)) {
     if (commit) {
       commit(false);
     } else {
       rollback();
     }
   } else {
     ArrayList<InDoubtTransaction> list = database.getInDoubtTransactions();
     int state = commit ? InDoubtTransaction.COMMIT : InDoubtTransaction.ROLLBACK;
     boolean found = false;
     if (list != null) {
       for (InDoubtTransaction p : list) {
         if (p.getTransactionName().equals(transactionName)) {
           p.setState(state);
           found = true;
           break;
         }
       }
     }
     if (!found) {
       throw DbException.get(ErrorCode.TRANSACTION_NOT_FOUND_1, transactionName);
     }
   }
 }
예제 #14
0
  /**
   * Split the password property into file password and user password if necessary, and convert them
   * to the internal hash format.
   */
  private void convertPasswords() {
    char[] password = removePassword();
    boolean passwordHash =
        removeProperty("PASSWORD_HASH", false); // 如果PASSWORD_HASH参数是true那么不再进行SHA256

    // 如果配置了CIPHER,则password包含两部份,用一个空格分开这两部份,第一部份是filePassword,第二部份是userPassword。
    // 如果PASSWORD_HASH参数是true那么不再进行SHA256,此时必须使用16进制字符,字符个数是偶数。
    // 如果PASSWORD_HASH参数是false,不必是16进制字符,会按SHA256算法进行hash
    if (getProperty("CIPHER", null) != null) {
      // split password into (filePassword+' '+userPassword)
      int space = -1;
      for (int i = 0, len = password.length; i < len; i++) {
        if (password[i] == ' ') {
          space = i;
          break;
        }
      }
      if (space < 0) {
        throw DbException.get(ErrorCode.WRONG_PASSWORD_FORMAT);
      }
      char[] np = new char[password.length - space - 1];
      char[] filePassword = new char[space];
      System.arraycopy(password, space + 1, np, 0, np.length);
      System.arraycopy(password, 0, filePassword, 0, space);
      Arrays.fill(password, (char) 0);
      password = np;
      // filePasswordHash用"file"进行hash
      fileEncryptionKey = FilePathEncrypt.getPasswordBytes(filePassword);
      filePasswordHash = hashPassword(passwordHash, "file", filePassword);
    }
    // userPasswordHash用用户名进行hash
    userPasswordHash = hashPassword(passwordHash, user, password);
  }
예제 #15
0
  private void readProperties(Properties info) {
    Object[] list = new Object[info.size()];
    info.keySet().toArray(list);
    DbSettings s = null;

    // 可在info中配三种参数,相关文档见:E:\H2\my-h2\my-h2-docs\999 可配置的参数汇总.java中的1、2、3项
    for (Object k : list) {
      String key = StringUtils.toUpperEnglish(k.toString());
      if (prop.containsKey(key)) {
        throw DbException.get(ErrorCode.DUPLICATE_PROPERTY_1, key);
      }
      Object value = info.get(k);
      // 支持org.h2.command.dml.SetTypes中的参数和ConnectionInfo与connectionTime相关的参数
      if (isKnownSetting(key)) {
        prop.put(key, value);
      } else {
        if (s == null) {
          s = getDbSettings();
        }
        // org.h2.constant.DbSettings中的参数
        if (s.containsKey(key)) {
          prop.put(key, value);
        }
      }
    }
  }
예제 #16
0
 /**
  * Sets the primary key columns, but also check if a primary key with different columns is already
  * defined.
  *
  * @param columns the primary key columns
  * @return true if the same primary key columns where already set
  */
 private boolean setPrimaryKeyColumns(IndexColumn[] columns) {
   if (pkColumns != null) {
     int len = columns.length;
     if (len != pkColumns.length) {
       throw DbException.get(ErrorCode.SECOND_PRIMARY_KEY);
     }
     for (int i = 0; i < len; i++) {
       if (!columns[i].columnName.equals(pkColumns[i].columnName)) {
         throw DbException.get(ErrorCode.SECOND_PRIMARY_KEY);
       }
     }
     return true;
   }
   this.pkColumns = columns;
   return false;
 }
예제 #17
0
 /**
  * Called to flush the output after data has been sent to the server and just before receiving
  * data. This method also reads the status code from the server and throws any exception the
  * server sent.
  *
  * @param transfer the transfer object
  * @throws DbException if the server sent an exception
  * @throws IOException if there is a communication problem between client and server
  */
 public void done(Transfer transfer) throws IOException {
   transfer.flush();
   int status = transfer.readInt();
   if (status == STATUS_ERROR) {
     String sqlstate = transfer.readString();
     String message = transfer.readString();
     String sql = transfer.readString();
     int errorCode = transfer.readInt();
     String stackTrace = transfer.readString();
     JdbcSQLException s =
         new JdbcSQLException(message, sql, sqlstate, errorCode, null, stackTrace);
     if (errorCode == ErrorCode.CONNECTION_BROKEN_1) {
       // allow re-connect
       IOException e = new IOException(s.toString(), s);
       throw e;
     }
     throw DbException.convert(s);
   } else if (status == STATUS_CLOSED) {
     transferList = null;
   } else if (status == STATUS_OK_STATE_CHANGED) {
     sessionStateChanged = true;
   } else if (status == STATUS_OK) {
     // ok
   } else {
     throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "unexpected status " + status);
   }
 }
 @Override
 public void createDirectory() {
   if (exists() && isDirectory()) {
     throw DbException.get(
         ErrorCode.FILE_CREATION_FAILED_1, name + " (a file with this name already exists)");
   }
   // TODO directories are not really supported
 }
예제 #19
0
 @Override
 public Value modulus(Value v) {
   ValueByte other = (ValueByte) v;
   if (other.value == 0) {
     throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
   }
   return ValueByte.get((byte) (value % other.value));
 }
예제 #20
0
 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();
 }
예제 #21
0
 @Override
 public long getRowCountApproximation() {
   try {
     return dataMap.map.sizeAsLong();
   } catch (IllegalStateException e) {
     throw DbException.get(ErrorCode.OBJECT_CLOSED);
   }
 }
예제 #22
0
 public int update() {
   session.getUser().checkAdmin();
   session.commit(true);
   Database db = session.getDatabase();
   Sequence sequence = getSchema().findSequence(sequenceName);
   if (sequence == null) {
     if (!ifExists) {
       throw DbException.get(ErrorCode.SEQUENCE_NOT_FOUND_1, sequenceName);
     }
   } else {
     if (sequence.getBelongsToTable()) {
       throw DbException.get(ErrorCode.SEQUENCE_BELONGS_TO_A_TABLE_1, sequenceName);
     }
     db.removeSchemaObject(session, sequence);
   }
   return 0;
 }
 private void copyData() {
   if (table.isTemporary()) {
     throw DbException.getUnsupportedException("TEMP TABLE");
   }
   Database db = session.getDatabase();
   String baseName = table.getName();
   String tempName = db.getTempTableName(baseName, session);
   Column[] columns = table.getColumns();
   ArrayList<Column> newColumns = New.arrayList();
   Table newTable = cloneTableStructure(columns, db, tempName, newColumns);
   try {
     // check if a view would become invalid
     // (because the column to drop is referenced or so)
     checkViews(table, newTable);
   } catch (DbException e) {
     execute("DROP TABLE " + newTable.getName(), true);
     throw DbException.get(ErrorCode.VIEW_IS_INVALID_2, e, getSQL(), e.getMessage());
   }
   String tableName = table.getName();
   ArrayList<TableView> views = table.getViews();
   if (views != null) {
     views = New.arrayList(views);
     for (TableView view : views) {
       table.removeView(view);
     }
   }
   execute("DROP TABLE " + table.getSQL() + " IGNORE", true);
   db.renameSchemaObject(session, newTable, tableName);
   for (DbObject child : newTable.getChildren()) {
     if (child instanceof Sequence) {
       continue;
     }
     String name = child.getName();
     if (name == null || child.getCreateSQL() == null) {
       continue;
     }
     if (name.startsWith(tempName + "_")) {
       name = name.substring(tempName.length() + 1);
       SchemaObject so = (SchemaObject) child;
       if (so instanceof Constraint) {
         if (so.getSchema().findConstraint(session, name) != null) {
           name = so.getSchema().getUniqueConstraintName(session, newTable);
         }
       } else if (so instanceof Index) {
         if (so.getSchema().findIndex(session, name) != null) {
           name = so.getSchema().getUniqueIndexName(session, newTable, name);
         }
       }
       db.renameSchemaObject(session, so, name);
     }
   }
   if (views != null) {
     for (TableView view : views) {
       String sql = view.getCreateSQL(true, true);
       execute(sql, true);
     }
   }
 }
예제 #24
0
 /**
  * Add a local temporary index to this session.
  *
  * @param index the index to add
  * @throws DbException if a index with this name already exists
  */
 public void addLocalTempTableIndex(Index index) {
   if (localTempTableIndexes == null) {
     localTempTableIndexes = database.newStringMap();
   }
   if (localTempTableIndexes.get(index.getName()) != null) {
     throw DbException.get(ErrorCode.INDEX_ALREADY_EXISTS_1, index.getSQL());
   }
   localTempTableIndexes.put(index.getName(), index);
 }
예제 #25
0
 @Override
 public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
   try {
     long cost = 10 * (dataMap.map.sizeAsLong() + Constants.COST_ROW_OFFSET);
     return cost;
   } catch (IllegalStateException e) {
     throw DbException.get(ErrorCode.OBJECT_CLOSED);
   }
 }
예제 #26
0
 /**
  * Create a duplicate key exception with a message that contains the index name.
  *
  * @param key the key values
  * @return the exception
  */
 protected DbException getDuplicateKeyException(String key) {
   String sql = getName() + " ON " + table.getSQL() + "(" + getColumnListSQL() + ")";
   if (key != null) {
     sql += " VALUES " + key;
   }
   DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
   e.setSource(this);
   return e;
 }
예제 #27
0
 /**
  * Keep a collection of the columns to pass to update if a duplicate key happens, for MySQL-style
  * INSERT ... ON DUPLICATE KEY UPDATE ....
  *
  * @param column the column
  * @param expression the expression
  */
 public void addAssignmentForDuplicate(Column column, Expression expression) {
   if (duplicateKeyAssignmentMap == null) {
     duplicateKeyAssignmentMap = New.hashMap();
   }
   if (duplicateKeyAssignmentMap.containsKey(column)) {
     throw DbException.get(ErrorCode.DUPLICATE_COLUMN_NAME_1, column.getName());
   }
   duplicateKeyAssignmentMap.put(column, expression);
 }
예제 #28
0
 /**
  * Add a local temporary table to this session.
  *
  * @param table the table to add
  * @throws DbException if a table with this name already exists
  */
 public void addLocalTempTable(Table table) {
   if (localTempTables == null) {
     localTempTables = database.newStringMap();
   }
   if (localTempTables.get(table.getName()) != null) {
     throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1, table.getSQL());
   }
   modificationId++;
   localTempTables.put(table.getName(), table);
 }
예제 #29
0
  @Override
  public void add(Session session, Row row) {
    if (mainIndexColumn == -1) {
      if (row.getKey() == 0) {
        row.setKey(++lastKey);
      }
    } else {
      long c = row.getValue(mainIndexColumn).getLong();
      row.setKey(c);
    }

    if (mvTable.getContainsLargeObject()) {
      for (int i = 0, len = row.getColumnCount(); i < len; i++) {
        Value v = row.getValue(i);
        Value v2 = v.link(database, getId());
        if (v2.isLinked()) {
          session.unlinkAtCommitStop(v2);
        }
        if (v != v2) {
          row.setValue(i, v2);
        }
      }
    }

    TransactionMap<Value, Value> map = getMap(session);
    Value key = ValueLong.get(row.getKey());
    Value old = map.getLatest(key);
    if (old != null) {
      String sql = "PRIMARY KEY ON " + table.getSQL();
      if (mainIndexColumn >= 0 && mainIndexColumn < indexColumns.length) {
        sql += "(" + indexColumns[mainIndexColumn].getSQL() + ")";
      }
      DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
      e.setSource(this);
      throw e;
    }
    try {
      map.put(key, ValueArray.get(row.getValueList()));
    } catch (IllegalStateException e) {
      throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
    }
    lastKey = Math.max(lastKey, row.getKey());
  }
예제 #30
0
 /**
  * Add a local temporary constraint to this session.
  *
  * @param constraint the constraint to add
  * @throws DbException if a constraint with the same name already exists
  */
 public void addLocalTempTableConstraint(Constraint constraint) {
   if (localTempTableConstraints == null) {
     localTempTableConstraints = database.newStringMap();
   }
   String name = constraint.getName();
   if (localTempTableConstraints.get(name) != null) {
     throw DbException.get(ErrorCode.CONSTRAINT_ALREADY_EXISTS_1, constraint.getSQL());
   }
   localTempTableConstraints.put(name, constraint);
 }