/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.jdbc;
import static org.junit.Assert.*;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.teiid.adminapi.impl.ModelMetaData;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.util.SimpleMock;
import org.teiid.core.util.UnitTestUtil;
import org.teiid.deployers.VDBRepository;
import org.teiid.metadata.Column;
import org.teiid.metadata.DuplicateRecordException;
import org.teiid.metadata.ForeignKey;
import org.teiid.metadata.FunctionMethod;
import org.teiid.metadata.MetadataFactory;
import org.teiid.metadata.MetadataStore;
import org.teiid.metadata.Procedure;
import org.teiid.metadata.ProcedureParameter;
import org.teiid.metadata.Table;
import org.teiid.query.metadata.NativeMetadataRepository;
import org.teiid.query.parser.QueryParser;
import org.teiid.translator.ExecutionFactory;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.jdbc.oracle.OracleExecutionFactory;
import org.teiid.translator.jdbc.teiid.TeiidExecutionFactory;
/**
*/
@SuppressWarnings("nls")
public class TestDynamicImportedMetaData {
public static final class BadMetadata {
public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException {
throw new SQLException();
}
}
private FakeServer server;
@Before public void setup() {
this.server = new FakeServer(true);
}
@After public void tearDown() {
this.server.stop();
}
private MetadataFactory getMetadata(Properties importProperties, Connection conn)
throws TranslatorException {
MetadataFactory mf = createMetadataFactory("test", importProperties);
TeiidExecutionFactory tef = new TeiidExecutionFactory();
tef.getMetadata(mf, conn);
return mf;
}
private MetadataFactory createMetadataFactory(String schema, Properties importProperties) {
VDBRepository vdbRepository = new VDBRepository();
return new MetadataFactory("vdb", 1, schema, vdbRepository.getRuntimeTypeMap(), importProperties, null);
}
@Test public void testUniqueReferencedKey() throws Exception {
server.deployVDB("vdb", UnitTestUtil.getTestDataPath() + "/keys.vdb");
Connection conn = server.createConnection("jdbc:teiid:vdb"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty("importer.importKeys", "true");
importProperties.setProperty("importer.schemaPattern", "x");
MetadataFactory mf = getMetadata(importProperties, conn);
Table t = mf.asMetadataStore().getSchemas().get("TEST").getTables().get("VDB.X.A");
List<ForeignKey> fks = t.getForeignKeys();
assertEquals(1, fks.size());
assertNotNull(fks.get(0).getPrimaryKey());
}
@Test public void testProcImport() throws Exception {
server.deployVDB("vdb", UnitTestUtil.getTestDataPath() + "/TestCase3473/test.vdb");
Connection conn = server.createConnection("jdbc:teiid:vdb"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty("importer.importProcedures", Boolean.TRUE.toString());
MetadataFactory mf = getMetadata(importProperties, conn);
Procedure p = mf.asMetadataStore().getSchemas().get("TEST").getProcedures().get("VDB.SYS.GETXMLSCHEMAS");
assertEquals(1, p.getResultSet().getColumns().size());
}
@Test public void testExcludes() throws Exception {
server.deployVDB("vdb", UnitTestUtil.getTestDataPath() + "/TestCase3473/test.vdb");
Connection conn = server.createConnection("jdbc:teiid:vdb"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty("importer.importProcedures", Boolean.TRUE.toString());
importProperties.setProperty("importer.excludeTables", "VDB\\.(SYS|pg_catalog).*");
importProperties.setProperty("importer.excludeProcedures", "VDB\\..*");
MetadataFactory mf = getMetadata(importProperties, conn);
assertEquals(String.valueOf(mf.asMetadataStore().getSchemas().get("TEST").getTables()), 3, mf.asMetadataStore().getSchemas().get("TEST").getTables().size());
assertEquals(0, mf.asMetadataStore().getSchemas().get("TEST").getProcedures().size());
}
@Test public void testDuplicateException() throws Exception {
MetadataFactory mf = createMetadataFactory("x", new Properties());
MetadataFactory mf1 = createMetadataFactory("y", new Properties());
Table dup = mf.addTable("dup");
Table dup1 = mf1.addTable("dup");
mf.addColumn("x", DataTypeManager.DefaultDataTypes.STRING, dup);
mf1.addColumn("x", DataTypeManager.DefaultDataTypes.STRING, dup1);
MetadataStore ms = mf.asMetadataStore();
ms.addSchema(mf1.asMetadataStore().getSchemas().values().iterator().next());
server.deployVDB("test", ms);
Connection conn = server.createConnection("jdbc:teiid:test"); //$NON-NLS-1$
Properties importProperties = new Properties();
mf = getMetadata(importProperties, conn);
Table t = mf.asMetadataStore().getSchemas().get("TEST").getTables().get("TEST.X.DUP");
assertEquals("\"test\".\"x\".\"dup\"", t.getNameInSource());
importProperties.setProperty("importer.useFullSchemaName", Boolean.FALSE.toString());
try {
getMetadata(importProperties, conn);
fail();
} catch (DuplicateRecordException e) {
}
}
@Test public void testUseCatalog() throws Exception {
MetadataFactory mf = createMetadataFactory("x", new Properties());
Table dup = mf.addTable("dup");
mf.addColumn("x", DataTypeManager.DefaultDataTypes.STRING, dup);
MetadataStore ms = mf.asMetadataStore();
server.deployVDB("test", ms);
Connection conn = server.createConnection("jdbc:teiid:test"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty("importer.useCatalogName", Boolean.FALSE.toString());
mf = getMetadata(importProperties, conn);
Table t = mf.asMetadataStore().getSchemas().get("TEST").getTables().get("X.DUP");
assertEquals("\"x\".\"dup\"", t.getNameInSource());
}
public static class DatabaseMetaDataProxy {
public String getCatalogSeparator() {
return ":";
}
}
public static class ConnectionProxy {
private final Connection conn;
private ConnectionProxy(Connection conn) {
this.conn = conn;
}
public DatabaseMetaData getMetaData() throws SQLException {
DatabaseMetaData dmd = conn.getMetaData();
dmd = (DatabaseMetaData) SimpleMock.createSimpleMock(new Object[] {new DatabaseMetaDataProxy(), dmd}, new Class<?>[] {DatabaseMetaData.class});
return dmd;
}
}
@Test public void testUseCatalogSeparator() throws Exception {
MetadataFactory mf = createMetadataFactory("x", new Properties());
Table dup = mf.addTable("dup");
mf.addColumn("x", DataTypeManager.DefaultDataTypes.STRING, dup);
MetadataStore ms = mf.asMetadataStore();
server.deployVDB("test", ms);
final Connection conn = server.createConnection("jdbc:teiid:test"); //$NON-NLS-1$
Properties importProperties = new Properties();
Connection conn1 = (Connection) SimpleMock.createSimpleMock(new Object[] {new ConnectionProxy(conn), conn}, new Class<?>[] {Connection.class});
mf = getMetadata(importProperties, conn1);
Table t = mf.asMetadataStore().getSchemas().get("TEST").getTables().get("test:X.DUP");
assertEquals("\"test\":\"x\".\"dup\"", t.getNameInSource());
}
@Test public void testUseQualified() throws Exception {
MetadataFactory mf = createMetadataFactory("x", new Properties());
Table dup = mf.addTable("dup");
mf.addColumn("x", DataTypeManager.DefaultDataTypes.STRING, dup);
MetadataStore ms = mf.asMetadataStore();
server.deployVDB("test", ms);
Connection conn = server.createConnection("jdbc:teiid:test"); //$NON-NLS-1$
//neither the name nor name in source should be qualified
Properties importProperties = new Properties();
importProperties.setProperty("importer.useQualifiedName", Boolean.FALSE.toString());
mf = getMetadata(importProperties, conn);
Table t = mf.asMetadataStore().getSchemas().get("TEST").getTables().get("DUP");
assertEquals("\"dup\"", t.getNameInSource());
}
@Test
public void testDDLMetadata() throws Exception {
String ddl = "CREATE FOREIGN PROCEDURE getTextFiles(IN pathAndPattern varchar) RETURNS (file clob, filpath string) OPTIONS(UUID 'uuid')";
MetadataFactory mf = createMetadataFactory("MarketData", new Properties());
QueryParser.getQueryParser().parseDDL(mf, ddl);
MetadataStore ms = mf.asMetadataStore();
String ddl2 = "CREATE VIEW stock (symbol string, price bigdecimal) OPTIONS (UUID 'uuid')" +
"AS select stock.* from (call MarketData.getTextFiles('*.txt')) f, " +
"TEXTTABLE(f.file COLUMNS symbol string, price bigdecimal HEADER) stock;";
MetadataFactory m2 = createMetadataFactory("portfolio", new Properties());
QueryParser.getQueryParser().parseDDL(m2, ddl2);
m2.getSchema().setPhysical(false);
m2.mergeInto(ms);
server.deployVDB("test", ms);
Connection conn = server.createConnection("jdbc:teiid:test"); //$NON-NLS-1$
Properties props = new Properties();
props.setProperty("importer.importProcedures", Boolean.TRUE.toString());
MetadataStore store = getMetadata(props, conn).asMetadataStore();
Procedure p = store.getSchema("test").getProcedure("test.MarketData.getTextFiles");
assertNotNull(p);
ProcedureParameter pp = p.getParameters().get(0);
assertEquals("pathAndPattern", pp.getName());
assertEquals("\"pathAndPattern\"", pp.getNameInSource());
assertEquals(ProcedureParameter.Type.In, pp.getType());
//assertEquals("string", pp.getDatatype().getName());
Table t = store.getSchema("test").getTable("test.portfolio.stock");
assertNotNull(t);
List<Column> columns = t.getColumns();
assertEquals(2, columns.size());
assertEquals("symbol", columns.get(0).getName());
assertEquals("price", columns.get(1).getName());
}
@Test public void testImportFunction() throws Exception {
MetadataFactory mf = createMetadataFactory("x", new Properties());
Table dup = mf.addTable("dup");
mf.addColumn("x", DataTypeManager.DefaultDataTypes.STRING, dup);
MetadataStore ms = mf.asMetadataStore();
server.deployVDB("test", ms);
Connection conn = server.createConnection("jdbc:teiid:test"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty(NativeMetadataRepository.IMPORT_PUSHDOWN_FUNCTIONS, Boolean.TRUE.toString());
mf = createMetadataFactory("test", importProperties);
NativeMetadataRepository nmr = new NativeMetadataRepository();
OracleExecutionFactory oef = new OracleExecutionFactory();
oef.start();
DataSource ds = Mockito.mock(DataSource.class);
Mockito.stub(ds.getConnection()).toReturn(conn);
nmr.loadMetadata(mf, oef, ds);
Map<String, FunctionMethod> functions = mf.asMetadataStore().getSchemas().get("TEST").getFunctions();
assertEquals(18, functions.size());
}
@Test public void testIgnorePkIndex() throws Exception {
MetadataFactory mf = createMetadataFactory("x", new Properties());
Table dup = mf.addTable("x");
mf.addColumn("x", DataTypeManager.DefaultDataTypes.STRING, dup);
mf.addPrimaryKey("foo", Arrays.asList("x"), dup);
MetadataStore ms = mf.asMetadataStore();
server.deployVDB("test", ms);
//cheat and add the index after deployment
mf.addIndex("foo", false, Arrays.asList("x"), dup);
Connection conn = server.createConnection("jdbc:teiid:test"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty("importer.importKeys", Boolean.TRUE.toString());
importProperties.setProperty("importer.importIndexes", Boolean.TRUE.toString());
mf = getMetadata(importProperties, conn);
Table t = mf.asMetadataStore().getSchemas().get("TEST").getTables().get("test.X.X");
assertNotNull(t.getPrimaryKey());
assertEquals(0, t.getUniqueKeys().size());
assertEquals(0, t.getIndexes().size());
}
@Test public void testDroppedFk() throws Exception {
ModelMetaData mmd = new ModelMetaData();
mmd.addSourceMetadata("ddl", "create foreign table x (y integer primary key);"
+ "create foreign table z (y integer, foreign key (y) references x)");
mmd.setName("foo");
mmd.addSourceMapping("x", "x", "x");
server.addTranslator("x", new ExecutionFactory());
server.deployVDB("vdb", mmd);
Connection conn = server.createConnection("jdbc:teiid:vdb"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty("importer.importKeys", "true");
//only import z and not the referenced table x
importProperties.setProperty("importer.tableNamePattern", "z");
MetadataFactory mf = getMetadata(importProperties, conn);
Table t = mf.asMetadataStore().getSchemas().get("test").getTables().get("vdb.foo.z");
List<ForeignKey> fks = t.getForeignKeys();
assertEquals(0, fks.size());
}
@Test public void testMultipleFK() throws Exception {
ModelMetaData mmd = new ModelMetaData();
mmd.addSourceMetadata("ddl", "create foreign table x (y integer, z integer, primary key (y, z));"
+ "create foreign table z (y integer, z integer, y1 integer, z1 integer, foreign key (y, z) references x (y, z), foreign key (y1, z1) references x (y, z))");
mmd.setName("foo");
mmd.addSourceMapping("x", "x", "x");
server.addTranslator("x", new ExecutionFactory());
server.deployVDB("vdb", mmd);
Connection conn = server.createConnection("jdbc:teiid:vdb"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty("importer.importKeys", "true");
MetadataFactory mf = getMetadata(importProperties, conn);
Table t = mf.asMetadataStore().getSchemas().get("test").getTables().get("vdb.foo.z");
List<ForeignKey> fks = t.getForeignKeys();
assertEquals(2, fks.size());
}
@Test public void testMultiSource() throws Exception {
ModelMetaData mmd = new ModelMetaData();
mmd.addSourceMetadata("ddl", "create foreign table x (y integer primary key);");
mmd.setName("foo");
mmd.addSourceMapping("x", "x", "x");
server.addTranslator("x", new ExecutionFactory());
server.deployVDB("vdb", mmd);
TeiidExecutionFactory tef = new TeiidExecutionFactory() {
@Override
public void closeConnection(Connection connection,
DataSource factory) {
}
};
tef.setSupportsDirectQueryProcedure(true);
tef.start();
server.addTranslator("teiid", tef);
DataSource ds = Mockito.mock(DataSource.class);
Mockito.stub(ds.getConnection()).toReturn(server.getDriver().connect("jdbc:teiid:vdb", null));
server.addConnectionFactory("teiid1", ds);
server.addConnectionFactory("teiid2", ds);
server.deployVDB(new FileInputStream(UnitTestUtil.getTestDataFile("multi.xml")));
Connection c = server.createConnection("jdbc:teiid:multi", null);
Statement s = c.createStatement();
s.execute("call native('select ?', 'b')");
ResultSet rs = s.getResultSet();
assertTrue(rs.next());
assertTrue(rs.next());
assertFalse(rs.next());
s.execute("call native(request=>'select ?', variable=>('b',), target=>'teiid1')");
rs = s.getResultSet();
assertTrue(rs.next());
Object[] result = (Object[]) rs.getArray(1).getArray();
assertArrayEquals(new Object[] {"b"}, result);
assertFalse(rs.next());
}
@Test public void testMultiSourceImportError() throws Exception {
ModelMetaData mmd = new ModelMetaData();
mmd.addSourceMetadata("ddl", "create foreign table x (y integer primary key);");
mmd.setName("foo");
mmd.addSourceMapping("x", "x", "x");
server.addTranslator("x", new ExecutionFactory());
server.deployVDB("vdb", mmd);
TeiidExecutionFactory tef = new TeiidExecutionFactory() {
@Override
public void closeConnection(Connection connection,
DataSource factory) {
}
};
tef.start();
server.addTranslator("teiid", tef);
DataSource ds = Mockito.mock(DataSource.class);
Connection c = server.getDriver().connect("jdbc:teiid:vdb", null);
//bad databasemetadata
DatabaseMetaData dbmd = (DatabaseMetaData) SimpleMock.createSimpleMock(new Object[] {new BadMetadata(), c.getMetaData()}, new Class[] {DatabaseMetaData.class});
Connection mock = Mockito.mock(Connection.class);
Mockito.stub(mock.getMetaData()).toReturn(dbmd);
Mockito.stub(ds.getConnection()).toReturn(mock);
DataSource ds1 = Mockito.mock(DataSource.class);
Mockito.stub(ds1.getConnection()).toReturn(server.getDriver().connect("jdbc:teiid:vdb", null));
server.addConnectionFactory("teiid1", ds);
server.addConnectionFactory("teiid2", ds1);
server.deployVDB(new FileInputStream(UnitTestUtil.getTestDataFile("multi.xml")));
}
@Test public void testUseScale() throws Exception {
MetadataFactory mf = createMetadataFactory("x", new Properties());
Table dup = mf.addTable("x");
Column c = mf.addColumn("x", DataTypeManager.DefaultDataTypes.BIG_DECIMAL, dup);
c.setPrecision(10);
c.setScale(2);
MetadataStore ms = mf.asMetadataStore();
server.deployVDB("test", ms);
Connection conn = server.createConnection("jdbc:teiid:test"); //$NON-NLS-1$
Properties importProperties = new Properties();
importProperties.setProperty("importer.useQualifiedName", Boolean.FALSE.toString());
mf = getMetadata(importProperties, conn);
Table t = mf.asMetadataStore().getSchemas().get("TEST").getTables().get("x");
c = t.getColumnByName("x");
assertEquals(10, c.getPrecision());
assertEquals(2, c.getScale());
}
}