/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* 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.jkiss.dbeaver.ext.generic.model.meta;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.ext.generic.GenericConstants;
import org.jkiss.dbeaver.ext.generic.model.*;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPErrorAssistant;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.exec.jdbc.JDBCResultSet;
import org.jkiss.dbeaver.model.exec.jdbc.JDBCSession;
import org.jkiss.dbeaver.model.exec.plan.DBCQueryPlanner;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCConstants;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
import org.jkiss.dbeaver.model.impl.jdbc.cache.JDBCBasicDataTypeCache;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.struct.rdb.DBSIndexType;
import org.jkiss.dbeaver.model.struct.rdb.DBSProcedureType;
import org.jkiss.utils.CommonUtils;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.*;
/**
* Generic meta model
*/
public class GenericMetaModel {
private static final Log log = Log.getLog(GenericMetaModel.class);
GenericMetaModelDescriptor descriptor;
public GenericMetaModel()
{
}
public GenericMetaObject getMetaObject(String id) {
return descriptor == null ? null : descriptor.getObject(id);
}
public GenericDataSource createDataSource(DBRProgressMonitor monitor, DBPDataSourceContainer container) throws DBException {
return new GenericDataSource(monitor, container, this, new GenericSQLDialect());
}
public void loadProcedures(DBRProgressMonitor monitor, @NotNull GenericObjectContainer container)
throws DBException
{
Map<String, GenericPackage> packageMap = null;
GenericDataSource dataSource = container.getDataSource();
GenericMetaObject procObject = dataSource.getMetaObject(GenericConstants.OBJECT_PROCEDURE);
try (JDBCSession session = DBUtils.openMetaSession(monitor, dataSource, "Load procedures")) {
// Read procedures
JDBCResultSet dbResult = session.getMetaData().getProcedures(
container.getCatalog() == null ? null : container.getCatalog().getName(),
container.getSchema() == null ? null : container.getSchema().getName(),
dataSource.getAllObjectsPattern());
try {
while (dbResult.next()) {
if (monitor.isCanceled()) {
break;
}
String procedureCatalog = GenericUtils.safeGetStringTrimmed(procObject, dbResult, JDBCConstants.PROCEDURE_CAT);
String procedureName = GenericUtils.safeGetStringTrimmed(procObject, dbResult, JDBCConstants.PROCEDURE_NAME);
String specificName = GenericUtils.safeGetStringTrimmed(procObject, dbResult, JDBCConstants.SPECIFIC_NAME);
int procTypeNum = GenericUtils.safeGetInt(procObject, dbResult, JDBCConstants.PROCEDURE_TYPE);
String remarks = GenericUtils.safeGetString(procObject, dbResult, JDBCConstants.REMARKS);
DBSProcedureType procedureType;
switch (procTypeNum) {
case DatabaseMetaData.procedureNoResult: procedureType = DBSProcedureType.PROCEDURE; break;
case DatabaseMetaData.procedureReturnsResult: procedureType = DBSProcedureType.FUNCTION; break;
case DatabaseMetaData.procedureResultUnknown: procedureType = DBSProcedureType.PROCEDURE; break;
default: procedureType = DBSProcedureType.UNKNOWN; break;
}
if (specificName == null && procedureName.indexOf(';') != -1) {
// [JDBC: SQL Server native driver]
specificName = procedureName;
procedureName = procedureName.substring(0, procedureName.lastIndexOf(';'));
}
// Check for packages. Oracle (and may be some other databases) uses catalog name as storage for package name
String packageName = null;
GenericPackage procedurePackage = null;
if (!CommonUtils.isEmpty(procedureCatalog) && CommonUtils.isEmpty(dataSource.getCatalogs())) {
// Catalog name specified while there are no catalogs in data source
packageName = procedureCatalog;
}
if (!CommonUtils.isEmpty(packageName)) {
if (packageMap == null) {
packageMap = new TreeMap<>();
}
procedurePackage = packageMap.get(packageName);
if (procedurePackage == null) {
procedurePackage = new GenericPackage(container, packageName, true);
packageMap.put(packageName, procedurePackage);
container.addPackage(procedurePackage);
}
}
final GenericProcedure procedure = createProcedureImpl(
procedurePackage != null ? procedurePackage : container,
procedureName,
specificName,
remarks,
procedureType,
null);
if (procedurePackage != null) {
procedurePackage.addProcedure(procedure);
} else {
container.addProcedure(procedure);
}
}
}
finally {
dbResult.close();
}
try {
// Try to read functions (note: this function appeared only in Java 1.6 so it maybe not implemented by many drivers)
// Read procedures
dbResult = session.getMetaData().getFunctions(
container.getCatalog() == null ? null : container.getCatalog().getName(),
container.getSchema() == null ? null : container.getSchema().getName(),
dataSource.getAllObjectsPattern());
try {
while (dbResult.next()) {
if (monitor.isCanceled()) {
break;
}
String functionName = GenericUtils.safeGetStringTrimmed(procObject, dbResult, JDBCConstants.FUNCTION_NAME);
if (functionName == null) {
//functionName = GenericUtils.safeGetStringTrimmed(procObject, dbResult, JDBCConstants.PROCEDURE_NAME);
// Apparently some drivers return the same results for getProcedures and getFunctions -
// so let's skip yet another procedure list
continue;
}
String specificName = GenericUtils.safeGetStringTrimmed(procObject, dbResult, JDBCConstants.SPECIFIC_NAME);
if (specificName == null && functionName.indexOf(';') != -1) {
// [JDBC: SQL Server native driver]
specificName = functionName;
functionName = functionName.substring(0, functionName.lastIndexOf(';'));
}
if (container.hasProcedure(functionName)) {
// Seems to be a duplicate
continue;
}
int funcTypeNum = GenericUtils.safeGetInt(procObject, dbResult, JDBCConstants.FUNCTION_TYPE);
String remarks = GenericUtils.safeGetString(procObject, dbResult, JDBCConstants.REMARKS);
GenericFunctionResultType functionResultType;
switch (funcTypeNum) {
//case DatabaseMetaData.functionResultUnknown: functionResultType = GenericFunctionResultType.UNKNOWN; break;
case DatabaseMetaData.functionNoTable: functionResultType = GenericFunctionResultType.NO_TABLE; break;
case DatabaseMetaData.functionReturnsTable: functionResultType = GenericFunctionResultType.TABLE; break;
default: functionResultType = GenericFunctionResultType.UNKNOWN; break;
}
final GenericProcedure procedure = createProcedureImpl(
container,
functionName,
specificName,
remarks,
DBSProcedureType.FUNCTION,
functionResultType);
container.addProcedure(procedure);
}
}
finally {
dbResult.close();
}
} catch (Throwable e) {
log.debug("Can't read generic functions", e);
}
} catch (SQLException e) {
throw new DBException(e, dataSource);
}
}
public GenericProcedure createProcedureImpl(
GenericStructContainer container,
String procedureName,
String specificName,
String remarks,
DBSProcedureType procedureType,
GenericFunctionResultType functionResultType)
{
return new GenericProcedure(
container,
procedureName,
specificName,
remarks,
procedureType,
functionResultType);
}
public String getViewDDL(DBRProgressMonitor monitor, GenericTable sourceObject) throws DBException {
return "-- View definition not available";
}
public String getTableDDL(DBRProgressMonitor monitor, GenericTable sourceObject) throws DBException {
return JDBCUtils.generateTableDDL(monitor, sourceObject, false);
}
public String getProcedureDDL(DBRProgressMonitor monitor, GenericProcedure sourceObject) throws DBException {
return "-- Source code not available";
}
public boolean supportsSequences(@NotNull GenericDataSource dataSource) {
return false;
}
public List<GenericSequence> loadSequences(@NotNull DBRProgressMonitor monitor, @NotNull GenericStructContainer container) throws DBException {
return new ArrayList<>();
}
public boolean supportsTriggers(@NotNull GenericDataSource dataSource) {
return false;
}
public boolean supportsDatabaseTriggers(@NotNull GenericDataSource dataSource) {
return false;
}
public List<? extends GenericTrigger> loadTriggers(DBRProgressMonitor monitor, @NotNull GenericStructContainer container, @Nullable GenericTable table) throws DBException {
return new ArrayList<>();
}
public String getTriggerDDL(@NotNull DBRProgressMonitor monitor, @NotNull GenericTrigger trigger) throws DBException {
return "-- Source code not available";
}
public JDBCBasicDataTypeCache createDataTypeCache(@NotNull DBPDataSourceContainer container) {
return new GenericDataTypeCache(container);
}
public DBCQueryPlanner getQueryPlanner(@NotNull GenericDataSource dataSource) {
return null;
}
public DBPErrorAssistant.ErrorPosition getErrorPosition(@NotNull Throwable error) {
return null;
}
public boolean supportsUpsertStatement() {
return false;
}
public GenericTable createTableImpl(
GenericStructContainer container,
@Nullable String tableName,
@Nullable String tableType,
@Nullable JDBCResultSet dbResult)
{
return new GenericTable(
container,
tableName,
tableType,
dbResult);
}
public GenericTableIndex createIndexImpl(
GenericTable table,
boolean nonUnique,
String qualifier,
long cardinality,
String indexName,
DBSIndexType indexType,
boolean persisted)
{
return new GenericTableIndex(
table,
nonUnique,
qualifier,
cardinality,
indexName,
indexType,
persisted);
}
/**
* Returns SQL clause for table column auto-increment.
* Null if auto-increment is not supported.
*/
public String getAutoIncrementClause(GenericTableColumn column) {
return null;
}
public boolean useCatalogInObjectNames() {
return true;
}
public boolean isSystemTable(GenericTable table) {
final String tableType = table.getTableType().toUpperCase(Locale.ENGLISH);
return tableType.contains("SYSTEM");
}
public boolean isView(GenericTable table) {
return table.getTableType().toUpperCase(Locale.ENGLISH).contains("VIEW");
}
}