public void validationPlanItem(PlanItem item) { int priority = item.getScanningStrategy().priority; if (tableRule instanceof ShardedTableRule) { ShardedTableRule shardedTableRule = (ShardedTableRule) tableRule; switch (shardedTableRule.getScanLevel()) { case ShardedTableRule.SCANLEVEL_SHARDINGKEY: if (priority < ScanningStrategy.USE_SHARDINGKEY.priority) { throw DbException.get( ErrorCode.ALLOWED_SCANTABLE_ERROR, getName(), "shardingKey", "shardingKey"); } break; case ShardedTableRule.SCANLEVEL_UNIQUEINDEX: if (priority < ScanningStrategy.USE_UNIQUEKEY.priority) { throw DbException.get( ErrorCode.ALLOWED_SCANTABLE_ERROR, getName(), "uniqueIndex", "uniqueIndex"); } break; case ShardedTableRule.SCANLEVEL_ANYINDEX: if (priority < ScanningStrategy.USE_INDEXKEY.priority) { throw DbException.get( ErrorCode.ALLOWED_SCANTABLE_ERROR, getName(), "indexKey", "indexKey"); } break; case ShardedTableRule.SCANLEVEL_UNLIMITED: break; default: throw DbException.throwInternalError("invalid scanLevel"); } } }
@Override public void prepare() { if (columns == null) { if (list.size() > 0 && list.get(0).length == 0) { // special case where table is used as a sequence columns = new Column[0]; } else { 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); } } }
/** * Each shard have a database connection, the worker thread may concurrently use a shard * connection executing SQL (such as executing ddl statement), If the JDBC driver is * spec-compliant, then technically yes, the object is thread-safe, MySQL Connector/J, all methods * to execute statements lock the connection with a synchronized block. * * @param options * @return */ protected Connection getConnectionWithStrategy(Options options) { String shardName = options.shardName; Connection conn; switch (holderStrategy) { case STRICTLY: if (connectionMap.isEmpty()) { conn = getRawConnection(options); connectionMap.put(shardName, conn); } else { conn = connectionMap.get(shardName); if (conn == null) { String lastTransactionNode = connectionMap.keySet().iterator().next(); throw DbException.get( ErrorCode.GENERAL_ERROR_1, "STRICTLY transaction mode not supported operation on multi-node, opend on " + lastTransactionNode + " and try on " + shardName); } } break; case ALLOW_CROSS_SHARD_READ: if (options.readOnly) { conn = connectionMap.get(shardName); if (conn == null) { conn = getRawConnectionForReadOnly(options); } } else { if (connectionMap.isEmpty()) { conn = getRawConnection(options); connectionMap.put(shardName, conn); } else { conn = connectionMap.get(shardName); if (conn == null) { String lastTransactionNode = connectionMap.keySet().iterator().next(); throw DbException.get( ErrorCode.GENERAL_ERROR_1, "ALLOW_READ_CROSS_DB transaction mode not supported writing operation on multi-node, opend on " + lastTransactionNode + " and try on " + shardName); } } } break; case BESTEFFORTS_1PC: conn = connectionMap.get(shardName); if (conn == null) { conn = getRawConnection(options); connectionMap.put(shardName, conn); } break; default: throw DbException.getInvalidValueException("transactionMode", holderStrategy); } return conn; }
private RoutingResult doRoute(Select prepare) { List<TableFilter> filters = filterNotTableMate(prepare.getTopTableFilter()); List<TableFilter> shards = New.arrayList(filters.size()); List<TableFilter> globals = New.arrayList(filters.size()); List<TableFilter> fixeds = New.arrayList(filters.size()); for (TableFilter tf : filters) { TableMate table = getTableMate(tf); switch (table.getTableRule().getType()) { case TableRule.SHARDED_NODE_TABLE: shards.add(tf); break; case TableRule.GLOBAL_NODE_TABLE: globals.add(tf); break; case TableRule.FIXED_NODE_TABLE: fixeds.add(tf); break; default: break; } } RoutingResult result = null; if (!shards.isEmpty()) { for (TableFilter f : shards) { f.setEvaluatable(f, false); if (f.isJoinOuter() || f.isJoinOuterIndirect()) { prepare.getCondition().createIndexConditions(session, f); } TableMate table = getTableMate(f); ConditionExtractor extractor = new ConditionExtractor(f); RoutingResult r = routingHandler.doRoute( table, extractor.getStart(), extractor.getEnd(), extractor.getInColumns()); result = (result == null || r.compareTo(result) < 0) ? r : result; } for (TableFilter f : shards) { f.setEvaluatable(f, true); } } else if (!fixeds.isEmpty()) { for (TableFilter tf : shards) { TableMate table = getTableMate(tf); RoutingResult r = routingHandler.doRoute(table); result = r; } } else if (!globals.isEmpty()) { // 全部为全局表查询,随机取一个第一个表结点 GlobalTableRule tableRule = (GlobalTableRule) getTableRule(globals.iterator().next()); RoutingResult r = tableRule.getRandomRoutingResult(); result = r; } else { throw DbException.throwInternalError("SQL_ROUTING_ERROR"); } ObjectNode[] selectNodes = result.getSelectNodes(); if (selectNodes.length == 0) { throw DbException.throwInternalError("SQL_ROUTING_ERROR,empty result"); } setConsistencyTableNodes(selectNodes, filters); return result; }
/** @param session */ public void readMataData(Session session, ObjectNode matadataNode) { for (int retry = 0; ; retry++) { try { Connection conn = null; String shardName = matadataNode.getShardName(); String tableName = matadataNode.getQualifiedObjectName(); String catalog = matadataNode.getCatalog(); String schema = matadataNode.getSchema(); try { JdbcRepository dsRepository = (JdbcRepository) database.getRepository(); DataSource dataSource = dsRepository.getDataSourceByShardName(shardName); conn = dataSource.getConnection(); tableName = database.identifier(tableName); if (catalog != null) { catalog = database.identifier(catalog); } if (schema != null) { schema = database.identifier(schema); } tryReadMetaData(conn, catalog, schema, tableName); return; } catch (Exception e) { throw DbException.convert(e); } finally { JdbcUtils.closeSilently(conn); } } catch (DbException e) { if (retry >= MAX_RETRY) { throw e; } } } }
/** * Remove the row from the result set if it exists. * * @param values the row */ public void removeDistinct(Value[] values) { if (!distinct) { DbException.throwInternalError(); } ValueArray array = ValueArray.get(values); distinctRows.remove(array); rowCount = distinctRows.size(); }
/** * Encode the string as an URL. * * @param s the string to encode * @return the encoded string */ public static String urlEncode(String s) { try { return URLEncoder.encode(s, "UTF-8"); } catch (Exception e) { // UnsupportedEncodingException throw DbException.convert(e); } }
/** * 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); }
/** * Add a row to this object. * * @param values the row to add */ @Override public void addRow(Value[] values) { cloneLobs(values); if (distinct) { ValueArray array = getArrayOfVisible(values); distinctRows.put(array, values); rowCount = distinctRows.size(); if (rowCount > maxMemoryRows) { throw DbException.getUnsupportedException("too big result row " + maxMemoryRows); } return; } rows.add(values); rowCount++; if (rows.size() > maxMemoryRows) { throw DbException.getUnsupportedException("too big result row " + maxMemoryRows); } }
private HolderStrategy transactionMode(String mode) { try { HolderStrategy holderStrategy = StringUtils.isNullOrEmpty(mode) ? HolderStrategy.BESTEFFORTS_1PC : HolderStrategy.valueOf(mode); return holderStrategy; } catch (Exception e) { throw DbException.getInvalidValueException("transactionMode", mode); } }
@Override protected void doPrepare() { expressions = prepared.getExpressions(); if (prepared.isGroupQuery()) { ArrayList<Expression> selectExprs = New.arrayList(10); int[] groupIndex = prepared.getGroupIndex(); for (int i = 0; i < groupIndex.length; i++) { int idx = groupIndex[i]; Expression expr = expressions.get(idx); selectExprs.add(expr); } HashSet<Aggregate> aggregates = New.hashSet(); for (Expression expr : expressions) { expr.isEverything(ExpressionVisitor.getAggregateVisitor(aggregates)); } selectExprs.addAll(aggregates); expressions = selectExprs; } Expression[] exprList = expressions.toArray(new Expression[expressions.size()]); Integer limit = null, offset = null; Expression limitExpr = prepared.getLimit(); Expression offsetExpr = prepared.getOffset(); if (limitExpr != null) { limit = limitExpr.getValue(session).getInt(); } if (offsetExpr != null) { offset = offsetExpr.getValue(session).getInt(); } RoutingResult rr = doRoute(prepared); if (rr.isMultipleNode() && offset != null) { if (offset > database.getSettings().analyzeSample) { throw DbException.get( ErrorCode.INVALID_VALUE_2, "offset", offset + ", the max support offset " + database.getSettings().analyzeSample + " is defined by analyzeSample."); } offset = offset != null ? 0 : offset; } ObjectNode[] selectNodes = rr.getSelectNodes(); if (session.getDatabase().getSettings().optimizeMerging) { selectNodes = rr.group(); } workers = New.arrayList(selectNodes.length); for (ObjectNode node : selectNodes) { QueryWorker queryHandler = queryHandlerFactory.createQueryWorker( prepared, node, consistencyTableNodes, exprList, limit, offset); workers.add(queryHandler); } }
public synchronized <T> List<T> foreach(Callback<T> callback) throws DbException { List<T> results = New.arrayList(); for (String name : connectionMap.keySet()) { try { Connection conn = connectionMap.get(name); results.add(callback.handle(name, conn)); } catch (SQLException e) { trace.error(e, "foreach {0} connection error", name); throw DbException.convert(e); } } return results; }
/** validation the rule columns is in the table columns */ private void setRuleColumns() { if (tableRule instanceof ShardedTableRule) { ShardedTableRule shardedTableRule = (ShardedTableRule) tableRule; String[] ruleColNames = shardedTableRule.getRuleColumns(); ruleColumns = new Column[ruleColNames.length]; for (int i = 0; i < ruleColNames.length; i++) { String colName = database.identifier(ruleColNames[i]); if (!doesColumnExist(colName)) { throw DbException.get(ErrorCode.SHARDING_COLUMN_NOT_FOUND, colName, getName()); } ruleColumns[i] = getColumn(colName); } } }
/** * Convert a hex encoded string to a byte array. * * @param s the hex encoded string * @return the byte array */ public static byte[] convertHexToBytes(String s) { int len = s.length(); if (len % 2 != 0) { throw DbException.get(ErrorCode.HEX_STRING_ODD_1, s); } len /= 2; byte[] buff = new byte[len]; int mask = 0; int[] hex = HEX_DECODE; try { for (int i = 0; i < len; i++) { int d = hex[s.charAt(i + i)] << 4 | hex[s.charAt(i + i + 1)]; mask |= d; buff[i] = (byte) d; } } catch (ArrayIndexOutOfBoundsException e) { throw DbException.get(ErrorCode.HEX_STRING_WRONG_1, s); } if ((mask & ~255) != 0) { throw DbException.get(ErrorCode.HEX_STRING_WRONG_1, s); } return buff; }
private Connection getRawConnectionForReadOnly(Options options) { try { Connection conn = target.getConnection(options); conn.setAutoCommit(true); conn.setReadOnly(true); if (session.getTransactionIsolation() != 0) { if (conn.getTransactionIsolation() != session.getTransactionIsolation()) { conn.setTransactionIsolation(session.getTransactionIsolation()); } } return conn; } catch (SQLException e) { throw DbException.convert(e); } }
public void loadMataData(Session session) { ObjectNode node = tableRule.getMetadataNode(); String tableName = node.getCompositeObjectName(); String shardName = node.getShardName(); try { trace.debug("Try to load {0} metadata from table {1}.{2}", getName(), shardName, tableName); readMataData(session, node); trace.debug("Load the {0} metadata success.", getName()); initException = null; } catch (DbException e) { if (e.getErrorCode() == ErrorCode.COLUMN_NOT_FOUND_1) { throw e; } trace.debug( "Fail to load {0} metadata from table {1}.{2}. error: {3}", getName(), shardName, tableName, e.getCause().getMessage()); initException = e; Column[] cols = {}; setColumns(cols); } if (isInited()) { setRuleColumns(); } }
/** * Construct a local result set by reading all data from a regular result set. * * @param session the session * @param rs the result set * @param maxrows the maximum number of rows to read (0 for no limit) * @return the local result set */ public static LocalResult read(Session session, ResultSet rs, int maxrows) { Expression[] cols = Expression.getExpressionColumns(session, rs); int columnCount = cols.length; LocalResult result = new LocalResult(session, cols, columnCount); try { for (int i = 0; (maxrows == 0 || i < maxrows) && rs.next(); i++) { Value[] list = new Value[columnCount]; for (int j = 0; j < columnCount; j++) { int type = result.getColumnType(j); list[j] = DataType.readValue(rs, j + 1, type); } result.addRow(list); } } catch (SQLException e) { throw DbException.convert(e); } result.done(); return result; }
/** * @param options * @return * @throws SQLException */ private Connection getRawConnection(Options options) throws DbException { Connection conn = target.getConnection(options); try { if (conn.getAutoCommit() != session.getAutoCommit()) { conn.setAutoCommit(session.getAutoCommit()); } if (session.getTransactionIsolation() != 0) { if (conn.getTransactionIsolation() != session.getTransactionIsolation()) { conn.setTransactionIsolation(session.getTransactionIsolation()); } } if (conn.isReadOnly() != session.isReadOnly()) { conn.setReadOnly(session.isReadOnly()); } } catch (Exception e) { throw DbException.convert(e); } return conn; }
@Override public boolean previous() { throw DbException.throwInternalError(); }
public void markDeleted() { Column[] cols = {}; setColumns(cols); indexes.clear(); initException = DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, this.getSQL()); }
private void tryReadMetaData(Connection conn, String oCatalog, String oSchema, String tableName) throws SQLException { DatabaseMetaData meta = conn.getMetaData(); storesLowerCase = meta.storesLowerCaseIdentifiers(); storesMixedCase = meta.storesMixedCaseIdentifiers(); storesMixedCaseQuoted = meta.storesMixedCaseQuotedIdentifiers(); supportsMixedCaseIdentifiers = meta.supportsMixedCaseIdentifiers(); ResultSet rs = meta.getTables(oCatalog, oSchema, tableName, null); if (rs.next() && rs.next()) { throw DbException.get(ErrorCode.SCHEMA_NAME_MUST_MATCH, tableName); } rs.close(); rs = meta.getColumns(null, null, tableName, null); int i = 0; ArrayList<Column> columnList = New.arrayList(); HashMap<String, Column> columnMap = New.hashMap(); String catalog = null, schema = null; while (rs.next()) { String thisCatalog = rs.getString("TABLE_CAT"); if (catalog == null) { catalog = thisCatalog; } String thisSchema = rs.getString("TABLE_SCHEM"); if (schema == null) { schema = thisSchema; } if (!StringUtils.equals(catalog, thisCatalog) || !StringUtils.equals(schema, thisSchema)) { // if the table exists in multiple schemas or tables, // use the alternative solution columnMap.clear(); columnList.clear(); break; } String n = rs.getString("COLUMN_NAME"); n = convertColumnName(n); int sqlType = rs.getInt("DATA_TYPE"); long precision = rs.getInt("COLUMN_SIZE"); precision = convertPrecision(sqlType, precision); int scale = rs.getInt("DECIMAL_DIGITS"); scale = convertScale(sqlType, scale); int displaySize = MathUtils.convertLongToInt(precision); int type = DataType.convertSQLTypeToValueType(sqlType); Column col = new Column(n, type, precision, scale, displaySize); col.setTable(this, i++); columnList.add(col); columnMap.put(n, col); } rs.close(); // check if the table is accessible Statement stat = null; try { stat = conn.createStatement(); rs = stat.executeQuery("SELECT * FROM " + tableName + " T WHERE 1=0"); if (columnList.size() == 0) { // alternative solution ResultSetMetaData rsMeta = rs.getMetaData(); for (i = 0; i < rsMeta.getColumnCount(); ) { String n = rsMeta.getColumnName(i + 1); n = convertColumnName(n); int sqlType = rsMeta.getColumnType(i + 1); long precision = rsMeta.getPrecision(i + 1); precision = convertPrecision(sqlType, precision); int scale = rsMeta.getScale(i + 1); scale = convertScale(sqlType, scale); int displaySize = rsMeta.getColumnDisplaySize(i + 1); int type = DataType.getValueTypeFromResultSet(rsMeta, i + 1); Column col = new Column(n, type, precision, scale, displaySize); col.setTable(this, i++); columnList.add(col); columnMap.put(n, col); } } rs.close(); } catch (Exception e) { throw DbException.get( ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, e, tableName + "(" + e.toString() + ")"); } finally { JdbcUtils.closeSilently(stat); } Column[] cols = new Column[columnList.size()]; columnList.toArray(cols); setColumns(cols); // create scan index // load primary keys try { rs = meta.getPrimaryKeys(null, null, tableName); } catch (Exception e) { // Some ODBC bridge drivers don't support it: // some combinations of "DataDirect SequeLink(R) for JDBC" // http://www.datadirect.com/index.ssp rs = null; } String pkName = ""; ArrayList<Column> list; if (rs != null && rs.next()) { // the problem is, the rows are not sorted by KEY_SEQ list = New.arrayList(); do { int idx = rs.getInt("KEY_SEQ"); if (pkName == null) { pkName = rs.getString("PK_NAME"); } while (list.size() < idx) { list.add(null); } String col = rs.getString("COLUMN_NAME"); col = convertColumnName(col); Column column = columnMap.get(col); if (idx == 0) { // workaround for a bug in the SQLite JDBC driver list.add(column); } else { list.set(idx - 1, column); } } while (rs.next()); addIndex(pkName, list, IndexType.createPrimaryKey(false)); rs.close(); } try { rs = meta.getIndexInfo(null, null, tableName, false, true); } catch (Exception e) { // Oracle throws an exception if the table is not found or is a // SYNONYM rs = null; } String indexName = null; list = New.arrayList(); IndexType indexType = null; if (rs != null) { while (rs.next()) { if (rs.getShort("TYPE") == DatabaseMetaData.tableIndexStatistic) { // ignore index statistics continue; } String newIndex = rs.getString("INDEX_NAME"); if (pkName.equals(newIndex)) { continue; } if (indexName != null && !indexName.equals(newIndex)) { addIndex(indexName, list, indexType); indexName = null; } if (indexName == null) { indexName = newIndex; list.clear(); } boolean unique = !rs.getBoolean("NON_UNIQUE"); indexType = unique ? IndexType.createUnique(false) : IndexType.createNonUnique(); String col = rs.getString("COLUMN_NAME"); col = convertColumnName(col); Column column = columnMap.get(col); list.add(column); } rs.close(); } if (indexName != null) { addIndex(indexName, list, indexType); } shardingKeyIndex(); }
private static DbException getFormatException(String s, int i) { return DbException.get(ErrorCode.STRING_FORMAT_ERROR_1, addAsterisk(s, i)); }