Right, the DataSource has only 2 methods, but to implement them I need to implement java.sql.Connection, too. And the latter will cause me to implement java.sql.Statement. Otherwise I have no place to insert my logic into.

Anyway, there is more important case: I generate inline model dynamically. So, I have no ability to use MyDataSource at all, because there is no model file to define MyDataSource class name at all. And the only option left is to extend calcite's Driver.

Thank you, Julian.

- Alexey.


On 10/04/2017 11:59 PM, Julian Hyde wrote:
DataSource only has two methods, so if you use it to connect to your underlying 
data source, accessing username and password or other authentication means from 
who knows where, then you wouldn’t have that much work to do.

Your approach might work too.

Julian


On Oct 3, 2017, at 4:25 AM, Alexey Roytman <[email protected]> wrote:

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