/* * Copyright 2011 Shunsuke Nakamura, and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cassandra.db.engine; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.Map; import java.util.HashMap; import java.nio.ByteBuffer; import org.handlersocket.*; import org.apache.cassandra.db.ColumnFamily; import org.apache.cassandra.db.DecoratedKey; public class HSMySQLInstance extends DBSchemafulInstance { HandlerSocket hsR, hsW; Connection conn; private final String PREFIX = "_"; private final String ID = "1"; private final String KEY = "rkey"; private final String VALUE = "cf"; private final String SYSTEM = "system"; private final String RANGEPR = "range_get_row"; private final String BINARY = "BINARY"; private final String BLOB = "BLOB"; private String rangeSt, truncateSt, dropTableSt, dropDBSt, createDBSt, rangePr; int debug = 0; public HSMySQLInstance(String ksName, String cfName) { this.ksName = ksName; this.cfName = PREFIX + cfName; setConfiguration(); setStatementDefinition(); createDB(); try { /* HandlerSocket uses three ports. */ conn = new MySQLConfigure().connect(this.ksName, host, 3306, user, pass); hsR = new HandlerSocket(); hsR.open(host, 9998); // default port 9998 hsR.command().openIndex(ID, this.ksName, this.cfName, "PRIMARY", KEY + "," + VALUE); hsR.execute(); hsW = new HandlerSocket(); hsW.open(host, 9999); // default port 9999 hsW.command().openIndex(ID, this.ksName, this.cfName, "PRIMARY", KEY + "," + VALUE); hsW.execute(); } catch (IOException e) { errorMsg("can't open hs", e); } } private void setStatementDefinition() { /* define statements */ rangeSt = !this.ksName.equals(SYSTEM) ? "CALL " + RANGEPR + this.cfName + "(?,?,?)" : "SELECT " + KEY + ", " + VALUE + " FROM " + this.cfName + " WHERE " + KEY + " >= ? AND " + KEY + " < ? LIMIT ?"; truncateSt = "TRUNCATE TABLE " + this.cfName; dropTableSt = "DROP TABLE IF EXISTS " + this.cfName; dropDBSt = "DROP DATABASE IF EXISTS " + this.ksName; createDBSt = "CREATE DATABASE IF NOT EXISTS " + this.ksName; rangePr = "CREATE PROCEDURE IF NOT EXISTS " + RANGEPR + this.cfName + "(IN begin VARCHAR(?),IN end VARCHAR(?),IN limitNum INT) BEGIN SET SQL_SELECT_LIMIT = limitNum; SELECT " + KEY + "," + VALUE + " FROM " + this.cfName + " WHERE " + KEY + " >= begin AND " + KEY + "< end; END"; } private String getCreateSt(String statement) { String createStHeader = "CREATE TABLE IF NOT EXISTS "+ this.cfName + "(" +"`" + KEY + "` VARCHAR(?) NOT NULL," + "`" + VALUE + "` "; String createStFooter = ", PRIMARY KEY (`" + KEY + "`)" + ") ENGINE = ?"; return createStHeader + statement + createStFooter; } public int insert(String rowKey, ColumnFamily cf) { try { return doInsert(rowKey, cf.toBytes()); } catch (IOException e) { return errorMsg("db insertion error", e); } } public int update(String rowKey, ColumnFamily newcf) { try { return doUpdate(rowKey, newcf.toBytes()); } catch (IOException e) { return errorMsg("db update error", e); } } public byte[] select(String rowKey) { try { hsR.command().find(ID, rowKey); List<HandlerSocketResult> res = hsR.execute(); return res.isEmpty() ? null : res.get(0).getMessages().get(1); } catch (IOException e) { errorMsg("db select error", e); return null; } } public Map<ByteBuffer, ColumnFamily> getRangeSlice(DecoratedKey startWith, DecoratedKey stopAt, int maxResults) { Map<ByteBuffer, ColumnFamily> rowMap = new HashMap<ByteBuffer, ColumnFamily>(); try { PreparedStatement pstRange = conn.prepareStatement(rangeSt); pstRange.setString(1, new String(startWith.getTokenBytes())); pstRange.setString(2, new String(stopAt.getTokenBytes())); pstRange.setInt(3, maxResults); ResultSet rs = pstRange.executeQuery(); if (rs != null) while (rs.next()) rowMap.put(ByteBuffer.wrap(rs.getBytes(1)), bytes2ColumnFamily(rs.getBytes(2))); rs.close(); pstRange.close(); return rowMap; } catch (SQLException e) { errorMsg("db get range slice error", e); } catch (IOException e) { errorMsg("db get range slice error", e); } return null; } public synchronized int delete(String rowKey) { try { hsW.command().findModifyDelete(ID, rowKey, "=", "1", "0"); List<HandlerSocketResult> Results = hsW.execute(); return SUCCESS; } catch (IOException e) { return errorMsg("db deletion error", e); } } public synchronized int truncate() { try { return conn.createStatement().executeUpdate(truncateSt); } catch (SQLException e) { return errorMsg("db truncation error", e); } } public synchronized int dropTable() { try { return conn.createStatement().executeUpdate(dropTableSt); } catch (SQLException e) { return errorMsg("db dropTable error", e); } } public synchronized int dropDB() { try { return conn.createStatement().executeUpdate(dropDBSt); } catch (SQLException e) { return errorMsg("db dropDB error" , e); } } // Init MySQL Table for ColumnFamily public synchronized int create(int rowKeySize, int columnFamilySize, String storageSize, String storageEngine) { try { return getCreatePreparedSt(rowKeySize, columnFamilySize, storageSize, storageEngine).executeUpdate(); } catch (SQLException e) { errorMsg("db table creation error", e); return -1; } } private PreparedStatement getCreatePreparedSt (int rowKeySize, int columnFamilySize, String storageSize, String storageEngine) { PreparedStatement pst = null; try { if (storageSize.contains(BLOB)) { pst = conn.prepareStatement(getCreateSt(storageSize)); pst.setInt(1, rowKeySize); pst.setString(2, storageEngine); } else { pst = conn.prepareStatement(getCreateSt(storageSize + "(?)")); pst.setInt(1, rowKeySize); pst.setInt(2, columnFamilySize); pst.setString(3, storageEngine); } } catch (SQLException e) { errorMsg("db table create statement error", e); } return pst; } public int createProcedure(int rowKeySize, int columnFamilySize) { try { Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SHOW PROCEDURE STATUS"); while (rs.next()) if (rs.getString(1).equals(ksName)) return 0; PreparedStatement rst = conn.prepareStatement(rangePr); rst.setInt(1, rowKeySize); rst.setInt(2, rowKeySize); return rst.executeUpdate(); } catch (SQLException e) { return errorMsg("db procedure creation error", e); } } public synchronized int createDB() { try { Statement stmt = new MySQLConfigure().connect("", host, port, user, pass).createStatement(); return stmt.executeUpdate(createDBSt); } catch (SQLException e) { return errorMsg("db database creation error", e); } } private int doInsert(String rowKey, byte[] cfValue) throws IOException { hsW.command().insert(ID, rowKey, cfValue); List<HandlerSocketResult> res = hsW.execute(); return res.get(0).getStatus(); //return doUpdate(rowKey, cfValue); } private synchronized int doUpdate(String rowKey, byte[] cfValue) throws IOException { hsW.command().findModifyUpdate(ID, rowKey, "=", "1", "0", cfValue); List<HandlerSocketResult> res = hsW.execute(); return res.get(0).getStatus(); } }