diff --git a/plugins/org.jkiss.dbeaver.ext.oceanbase/plugin.xml b/plugins/org.jkiss.dbeaver.ext.oceanbase/plugin.xml index 9a24cc884ac68fdc0182222bffc2518eea476b24..4201ce4da02f2fbe7876cad520ade9801bfb6780 100644 --- a/plugins/org.jkiss.dbeaver.ext.oceanbase/plugin.xml +++ b/plugins/org.jkiss.dbeaver.ext.oceanbase/plugin.xml @@ -16,7 +16,7 @@ icon="icons/ob_icon.png" label="OceanBase data source" path="oceanbase"> - + @@ -68,6 +68,12 @@ + + + + + + @@ -152,7 +158,10 @@ - + @@ -193,4 +202,8 @@ uiClass="org.jkiss.dbeaver.ui.dialogs.connection.DatabaseNativeAuthModelConfigurator"/> + + + + diff --git a/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLCatalog.java b/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLCatalog.java new file mode 100644 index 0000000000000000000000000000000000000000..65395310018204d23933a80e3c45f726f8e1b298 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLCatalog.java @@ -0,0 +1,183 @@ +package org.jkiss.dbeaver.ext.oceanbase.mysql.model; + +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.ext.mysql.MySQLConstants; +import org.jkiss.dbeaver.ext.mysql.model.MySQLCatalog; +import org.jkiss.dbeaver.ext.mysql.model.MySQLDataSource; +import org.jkiss.dbeaver.ext.mysql.model.MySQLProcedure; +import org.jkiss.dbeaver.ext.mysql.model.MySQLProcedureParameter; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCPreparedStatement; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCResultSet; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCSession; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCStatement; +import org.jkiss.dbeaver.model.impl.jdbc.JDBCConstants; +import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils; +import org.jkiss.dbeaver.model.impl.jdbc.cache.JDBCStructLookupCache; +import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; +import org.jkiss.dbeaver.model.struct.DBSObject; +import org.jkiss.dbeaver.model.struct.rdb.DBSProcedureParameterKind; +import org.jkiss.dbeaver.model.struct.rdb.DBSProcedureType; +import org.jkiss.utils.CommonUtils; + +public class OceanbaseMySQLCatalog extends MySQLCatalog{ + private final OceanbaseMySQLDataSource dataSource; + final OceanbaseProceduresCache oceanbaseProceduresCache = new OceanbaseProceduresCache(); + + static class OceanbaseProceduresCache extends JDBCStructLookupCache { + + public OceanbaseProceduresCache() { + super(JDBCConstants.PROCEDURE_NAME); + } + + @Override + public JDBCStatement prepareLookupStatement(JDBCSession session, OceanbaseMySQLCatalog owner, OceanbaseMySQLProcedure object, + String objectName) throws SQLException { + JDBCPreparedStatement dbStat = session.prepareStatement( + "SELECT * FROM " + MySQLConstants.META_TABLE_ROUTINES + + "\nWHERE " + MySQLConstants.COL_ROUTINE_SCHEMA + "=?" + + (object == null && objectName == null ? "" : " AND " + MySQLConstants.COL_ROUTINE_NAME + "=?") + + " AND ROUTINE_TYPE" + (object == null ? " IN ('PROCEDURE','FUNCTION')" : "=?") + + "\nORDER BY " + MySQLConstants.COL_ROUTINE_NAME + ); + dbStat.setString(1, owner.getName()); + if (object != null || objectName != null) { + dbStat.setString(2, object != null ? object.getName() : objectName); + if (object != null) { + dbStat.setString(3, String.valueOf(object.getProcedureType())); + } + } + return dbStat; + } + + @Override + protected JDBCStatement prepareChildrenStatement(JDBCSession session, OceanbaseMySQLCatalog owner, + OceanbaseMySQLProcedure procedure) throws SQLException { + if(procedure.getProcedureType().equals(DBSProcedureType.PROCEDURE)) { + return session.getMetaData().getProcedureColumns( + owner.getName(), + null, + procedure == null ? null : JDBCUtils.escapeWildCards(session, procedure.getName()), + "%").getSourceStatement(); + } + else { + String queryFunctionString = "select * from mysql.proc where db='%s' and type='FUNCTION' and name='%s'"; + return session.prepareStatement(String.format(queryFunctionString, owner.getName(), procedure.getName())); + } + } + + @Override + protected MySQLProcedureParameter fetchChild(JDBCSession session, OceanbaseMySQLCatalog owner, OceanbaseMySQLProcedure parent, + JDBCResultSet dbResult) throws SQLException, DBException { + if(parent.getProcedureType().equals(DBSProcedureType.PROCEDURE)) { + String columnName = JDBCUtils.safeGetString(dbResult, JDBCConstants.COLUMN_NAME); + int columnTypeNum = JDBCUtils.safeGetInt(dbResult, JDBCConstants.COLUMN_TYPE); + int valueType = JDBCUtils.safeGetInt(dbResult, JDBCConstants.DATA_TYPE); + String typeName = JDBCUtils.safeGetString(dbResult, JDBCConstants.TYPE_NAME); + int position = JDBCUtils.safeGetInt(dbResult, JDBCConstants.ORDINAL_POSITION); + long columnSize = JDBCUtils.safeGetLong(dbResult, JDBCConstants.LENGTH); + boolean notNull = JDBCUtils.safeGetInt(dbResult, JDBCConstants.NULLABLE) == DatabaseMetaData.procedureNoNulls; + int scale = JDBCUtils.safeGetInt(dbResult, JDBCConstants.SCALE); + int precision = JDBCUtils.safeGetInt(dbResult, JDBCConstants.PRECISION); + DBSProcedureParameterKind parameterType; + switch (columnTypeNum) { + case DatabaseMetaData.procedureColumnIn: parameterType = DBSProcedureParameterKind.IN; break; + case DatabaseMetaData.procedureColumnInOut: parameterType = DBSProcedureParameterKind.INOUT; break; + case DatabaseMetaData.procedureColumnOut: parameterType = DBSProcedureParameterKind.OUT; break; + case DatabaseMetaData.procedureColumnReturn: parameterType = DBSProcedureParameterKind.RETURN; break; + case DatabaseMetaData.procedureColumnResult: parameterType = DBSProcedureParameterKind.RESULTSET; break; + default: parameterType = DBSProcedureParameterKind.UNKNOWN; break; + } + if (CommonUtils.isEmpty(columnName) && parameterType == DBSProcedureParameterKind.RETURN) { + columnName = "RETURN"; + } + return new MySQLProcedureParameter( + parent, + columnName, + typeName, + valueType, + position, + columnSize, + scale, precision, notNull, + parameterType); + } else { + String[] paramList = JDBCUtils.safeGetString(dbResult, "returns").split("\\("); + int columnSize = Integer.parseInt(paramList[1].split("\\)")[0]); + + return new MySQLProcedureParameter( + parent, + "RETURN", + paramList[0], + STRUCT_ATTRIBUTES, + 0, + columnSize, + null, + null, + true, + null); + } + } + + @Override + protected OceanbaseMySQLProcedure fetchObject(JDBCSession session, OceanbaseMySQLCatalog owner, JDBCResultSet resultSet) + throws SQLException, DBException { + return new OceanbaseMySQLProcedure(owner, resultSet); + } + + } + + public OceanbaseMySQLCatalog(OceanbaseMySQLDataSource dataSource, ResultSet dbResult) { + super(dataSource, dbResult); + this.dataSource = dataSource; + } + + public OceanbaseProceduresCache getOceanbaseProceduresCache() { + return this.oceanbaseProceduresCache; + } + + @Override + public Collection getProcedures(DBRProgressMonitor monitor) throws DBException { + if(!getDataSource().supportsInformationSchema()) { + return Collections.emptyList(); + } + List objects = new ArrayList<>(); + for(OceanbaseMySQLProcedure oceanbaseMySQLProcedure : oceanbaseProceduresCache.getAllObjects(monitor, this)) { + objects.add(oceanbaseMySQLProcedure); + } + return objects; + } + + @Override + public MySQLProcedure getProcedure(DBRProgressMonitor monitor, String procName) + throws DBException + { + return oceanbaseProceduresCache.getObject(monitor, this, procName); + } + + @Override + public synchronized DBSObject refreshObject(@NotNull DBRProgressMonitor monitor) + throws DBException + { + super.refreshObject(monitor); + oceanbaseProceduresCache.clearCache(); + return this; + } + + @NotNull + @Override + public MySQLDataSource getDataSource() + { + return (OceanbaseMySQLDataSource)dataSource; + } + + + +} diff --git a/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLDataSource.java b/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLDataSource.java index eedcac6da249ee767a9e993365a4715dae8fb802..67d6118c25885be25162b5675e7fa3528a2e9c29 100644 --- a/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLDataSource.java +++ b/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLDataSource.java @@ -16,32 +16,50 @@ */ package org.jkiss.dbeaver.ext.oceanbase.mysql.model; +import java.sql.SQLException; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.ext.mysql.MySQLConstants; +import org.jkiss.dbeaver.ext.mysql.model.MySQLCatalog; import org.jkiss.dbeaver.ext.mysql.model.MySQLDataSource; import org.jkiss.dbeaver.ext.mysql.model.MySQLEngine; import org.jkiss.dbeaver.ext.mysql.model.MySQLPrivilege; +import org.jkiss.dbeaver.ext.mysql.model.MySQLTable; import org.jkiss.dbeaver.ext.oceanbase.model.plan.OceanbasePlanAnalyzer; import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.data.DBDValueHandlerProvider; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCPreparedStatement; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCResultSet; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCSession; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCStatement; import org.jkiss.dbeaver.model.exec.plan.DBCQueryPlanner; +import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils; import org.jkiss.dbeaver.model.impl.jdbc.cache.JDBCBasicDataTypeCache; +import org.jkiss.dbeaver.model.impl.jdbc.cache.JDBCObjectCache; import org.jkiss.dbeaver.model.impl.jdbc.data.handlers.JDBCStandardValueHandlerProvider; import org.jkiss.dbeaver.model.impl.jdbc.struct.JDBCDataType; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.struct.DBSDataType; +import org.jkiss.dbeaver.model.struct.DBSObjectFilter; +import org.jkiss.utils.CommonUtils; public class OceanbaseMySQLDataSource extends MySQLDataSource { private final JDBCBasicDataTypeCache dataTypeCache; + private final String tenantType; + private final OceanbaseCatalogCache oceanbaseCatalogCache = new OceanbaseCatalogCache(); public OceanbaseMySQLDataSource(DBRProgressMonitor monitor, DBPDataSourceContainer container) throws DBException { super(monitor, container); dataTypeCache = new JDBCBasicDataTypeCache<>(this); + tenantType = container.getConnectionConfiguration().getProviderProperty("tenantType"); } @Override @@ -49,6 +67,8 @@ public class OceanbaseMySQLDataSource extends MySQLDataSource { throws DBException { super.initialize(monitor); dataTypeCache.getAllObjects(monitor, this); + // Read catalogs + oceanbaseCatalogCache.getAllObjects(monitor, this); } @Override @@ -97,4 +117,85 @@ public class OceanbaseMySQLDataSource extends MySQLDataSource { public DBSDataType getLocalDataType(int typeID) { return dataTypeCache.getCachedObject(typeID); } + + public boolean isMySQLMode () { + return tenantType.equals("MySQL"); + } + + MySQLTable findTable(DBRProgressMonitor monitor, String catalogName, String tableName)throws DBException { + if (CommonUtils.isEmpty(catalogName)) { + return null; + } + OceanbaseMySQLCatalog catalog = (OceanbaseMySQLCatalog)getCatalog(catalogName); + if (catalog == null) { + return null; + } + return catalog.getTable(monitor, tableName); + } + + @Override + public Collection getChildren(@NotNull DBRProgressMonitor monitor) + throws DBException { + return getCatalogs(); + } + + @Override + public MySQLCatalog getChild(@NotNull DBRProgressMonitor monitor, @NotNull String childName) + throws DBException { + return getCatalog(childName); + } + + @NotNull + @Override + public Class getPrimaryChildType(@Nullable DBRProgressMonitor monitor) + throws DBException { + return OceanbaseMySQLCatalog.class; + } + + public OceanbaseCatalogCache getOceanbaseCatalogCache() { + return oceanbaseCatalogCache; + } + + @Override + public MySQLCatalog getCatalog(String name) { + return oceanbaseCatalogCache.getCachedObject(name); + } + + @Override + public Collection getCatalogs() { + List catalogs = new ArrayList<>(); + for (OceanbaseMySQLCatalog oceanbaseMySQLCatalog : oceanbaseCatalogCache.getCachedObjects()) { + catalogs.add(oceanbaseMySQLCatalog); + } + return catalogs; + } + + static class OceanbaseCatalogCache extends JDBCObjectCache { + @NotNull + @Override + protected JDBCStatement prepareObjectsStatement(@NotNull JDBCSession session, @NotNull OceanbaseMySQLDataSource owner) throws SQLException { + StringBuilder catalogQuery = new StringBuilder("show databases"); + DBSObjectFilter catalogFilters = owner.getContainer().getObjectFilter(MySQLCatalog.class, null, false); + if (catalogFilters != null) { + JDBCUtils.appendFilterClause(catalogQuery, catalogFilters, MySQLConstants.COL_DATABASE_NAME, true); + } + JDBCPreparedStatement dbStat = session.prepareStatement(catalogQuery.toString()); + if (catalogFilters != null) { + JDBCUtils.setFilterParameters(dbStat, 1, catalogFilters); + } + return dbStat; + } + + @Override + protected OceanbaseMySQLCatalog fetchObject(@NotNull JDBCSession session, @NotNull OceanbaseMySQLDataSource owner, @NotNull JDBCResultSet resultSet) throws SQLException, DBException { + return new OceanbaseMySQLCatalog(owner, resultSet); + } + + } + + @Override + public boolean supportsInformationSchema() { + return true; + } + } diff --git a/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLDatabaseManager.java b/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLDatabaseManager.java new file mode 100644 index 0000000000000000000000000000000000000000..83b91b5b8a3cf97f0d52e04fb5f0d91b3a05f399 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLDatabaseManager.java @@ -0,0 +1,73 @@ +package org.jkiss.dbeaver.ext.oceanbase.mysql.model; + +import java.util.List; +import java.util.Map; + +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.DBPDataSource; +import org.jkiss.dbeaver.model.edit.DBECommandContext; +import org.jkiss.dbeaver.model.edit.DBEObjectRenamer; +import org.jkiss.dbeaver.model.edit.DBEPersistAction; +import org.jkiss.dbeaver.model.exec.DBCExecutionContext; +import org.jkiss.dbeaver.model.impl.edit.SQLDatabasePersistAction; +import org.jkiss.dbeaver.model.impl.sql.edit.SQLObjectEditor; +import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; +import org.jkiss.dbeaver.model.struct.DBSObject; +import org.jkiss.dbeaver.model.struct.cache.DBSObjectCache; + +public class OceanbaseMySQLDatabaseManager extends SQLObjectEditor implements DBEObjectRenamer{ + + @Override + public long getMakerOptions(DBPDataSource dataSource) { + return FEATURE_SAVE_IMMEDIATELY; + } + + @Override + public DBSObjectCache getObjectsCache(OceanbaseMySQLCatalog object) { + return ((OceanbaseMySQLDataSource)object.getDataSource()).getOceanbaseCatalogCache(); + } + + @Override + public void renameObject(DBECommandContext commandContext, OceanbaseMySQLCatalog object, + Map options, String newName) throws DBException { + throw new DBException("Direct database rename is not yet implemented in MySQL. You should use export/import functions for that."); + } + + @Override + protected OceanbaseMySQLCatalog createDatabaseObject(DBRProgressMonitor monitor, DBECommandContext context, + Object container, Object copyFrom, Map options) throws DBException { + System.out.println("11111"); + return new OceanbaseMySQLCatalog((OceanbaseMySQLDataSource) container, null); + } + + @Override + protected void addObjectCreateActions(DBRProgressMonitor monitor, DBCExecutionContext executionContext, + List actions, + SQLObjectEditor.ObjectCreateCommand command, + Map options) throws DBException { + final OceanbaseMySQLCatalog catalog = command.getObject(); + final StringBuilder script = new StringBuilder("CREATE SCHEMA `" + catalog.getName() + "`"); + appendDatabaseModifiers(catalog, script); + actions.add( + new SQLDatabasePersistAction("Create schema", script.toString()) //$NON-NLS-2$ + ); + } + + @Override + protected void addObjectDeleteActions(DBRProgressMonitor monitor, DBCExecutionContext executionContext, + List actions, + SQLObjectEditor.ObjectDeleteCommand command, + Map options) throws DBException { + actions.add(new SQLDatabasePersistAction("Drop schema", "DROP SCHEMA `" + command.getObject().getName() + "`")); + } + + private void appendDatabaseModifiers(OceanbaseMySQLCatalog catalog, StringBuilder script) { + if (catalog.getAdditionalInfo().getDefaultCharset() != null) { + script.append("\nDEFAULT CHARACTER SET ").append(catalog.getAdditionalInfo().getDefaultCharset().getName()); + } + if (catalog.getAdditionalInfo().getDefaultCollation() != null) { + script.append("\nDEFAULT COLLATE ").append(catalog.getAdditionalInfo().getDefaultCollation().getName()); + } + } + +} diff --git a/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLProcedure.java b/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLProcedure.java new file mode 100644 index 0000000000000000000000000000000000000000..f23cf79e40801ea5e9127961c5ed28ded1eda9d3 --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ext.oceanbase/src/org/jkiss/dbeaver/ext/oceanbase/mysql/model/OceanbaseMySQLProcedure.java @@ -0,0 +1,36 @@ +package org.jkiss.dbeaver.ext.oceanbase.mysql.model; + +import java.sql.ResultSet; +import java.util.Collection; +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.ext.mysql.model.MySQLCatalog; +import org.jkiss.dbeaver.ext.mysql.model.MySQLProcedure; +import org.jkiss.dbeaver.ext.mysql.model.MySQLProcedureParameter; +import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; +import org.jkiss.dbeaver.model.struct.DBSObject; + +public class OceanbaseMySQLProcedure extends MySQLProcedure{ + private OceanbaseMySQLCatalog container = (OceanbaseMySQLCatalog)getContainer(); + + public OceanbaseMySQLProcedure(MySQLCatalog catalog) { + super(catalog); + } + + public OceanbaseMySQLProcedure(MySQLCatalog catalog, ResultSet dbResult) { + super(catalog, dbResult); + } + + @Override + public Collection getParameters(DBRProgressMonitor monitor) + throws DBException + { + return container.oceanbaseProceduresCache.getChildren(monitor, container, this); + } + + @Override + public DBSObject refreshObject(@NotNull DBRProgressMonitor monitor) throws DBException { + return container.oceanbaseProceduresCache.refreshObject(monitor, container, this); + } + +}