I did not take the approach of implementing javax.sql.DataSource as this causes implementation of java.sql.Connection and java.sql.Statement; and I want to use Calcite framework for parsing and executing SQL in Statement.

So, the code I use now is as following:

public class MyDriver extends org.apache.calcite.jdbc.Driver {
  public static final String CONNECT_STRING_PREFIX = "jdbc:my:";
  static { new MyDriver().register(); }
  public MyDriver() { super(); }
  @Override protected String getConnectStringPrefix() { return CONNECT_STRING_PREFIX; }   private static ThreadLocal<String> cached_username = new ThreadLocal<String>();   private static ThreadLocal<String> cached_password = new ThreadLocal<String>();   public static String getCachedUsername() { return cached_username.get(); } // used by SchemaFactory.create()   public static String getCachedPassword() { return cached_password.get(); } // used by SchemaFactory.create()
  @Override
  public java.sql.Connection connect(String url, Properties info) throws SQLException {
      if (info != null) {
          // same properties as in DriverManager.getConnection(String url, String username, String password)
          String username = info.getProperty("user");
          String password = info.getProperty("password");
          if (username != null && password != null) {
              cached_username.set(username);
              cached_password.set(password);
          }
      }
      return super.connect(url, info);
  }
}
public class MySchemaFactory implements SchemaFactory {
    public MySchemaFactory() {}
    @Override
    public Schema create(SchemaPlus schemaPlus, String s, Map<String, Object> map) {
        String url = (String)map.get("url");
        String username = (String)map.get("username");
        String password = (String)map.get("password");
        if (username == null && password == null) {
            username = MyDriver.getCachedUsername();
            password = MyDriver.getCachedPassword();
        }
        // ... here we do some real work, but for simplicity we...
        return null;
    }
}
// and then in the main code...
String user;
String password;
String url;
// got user, password and url from somewhere...
// url= "jdbc:my:schemaFactory=MySchemaFactory;schema=demodb;schema.url=..."
Class.forName("MyDriver");
java.sql.Connection conn = DriverManager.getConnection(url, user, password);
...


The questions are:
Q1. Is this approach too ugly?
Q2. Is this approach against the ideology of Calcite?
Q3. Can I have more then one schema just by url tags, without model file/inline/Properties-argument?

- Alexey.


On 10/01/2017 03:06 PM, Alexey Roytman wrote:
Dear colleagues, I'm doing my first steps in creating custom SchemaFactory for accessing some endpoint.
My question is as following:
     if I do DriverManager.getConnection(url, user, password);
why then:
     SchemaFactory.create(SchemaPlus, String, Map<String, Object>)
gets the map without user and password?

I shall not create a model file (because it's a plain text with password); I shall not pass username and password in URL (e.g. ";schema.username=...;schema.password=..." or model inline) because it's saved as plain text.

I cannot change the call from DriverManager.getConnection(url, user, password) to DriverManager.getConnection(url, Properties info) with custom Properties, as I don't control that code.

My test code basically does this:
    String user;
    String password;
    String url;
    ...
    // got user, password and url from somewhere
    // the url is of form: "jdbc:calcite:schemaFactory=MySchemaFactory"
    Class.forName("org.apache.calcite.jdbc.Driver");
    java.sql.Connection conn = DriverManager.getConnection(url, user, password);

And the MySchemaFactory.java looks like this:
---
    public class MySchemaFactory implements org.apache.calcite.schema.SchemaFactory {
        public MySchemaFactory() {
            System.out.println("Factory");
        }
        @Override
        public Schema create(SchemaPlus schemaPlus, String s, java.util.Map<String, Object> map) {
            String url = (String)map.get("url");
            String username = (String)map.get("username");
            String password = (String)map.get("password");
            // do something with url, username and password...
            return null; // just for demonstration
        }
    }
---

Instead of MySchemaFactory I can use org.apache.calcite.adapter.cassandra.CassandraSchemaFactory, with same result: the map has no credentials.

Does anyone have any idea of a secure way of passing credentials?..
Maybe, I need to write my own class extending org.apache.calcite.jdbc.Driver?
What the right Calcite's way?

- Alexey.


Reply via email to