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"); } } }
/** * 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; }
@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); } } }
/** * 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); }
@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); } }
/** 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; }
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)); }