-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Function resolution #12743
Function resolution #12743
Conversation
e0854ad
to
b455821
Compare
7f96068
to
2d8b8a9
Compare
2d8b8a9
to
13b5fd8
Compare
44f40b4
to
9e9667a
Compare
I'll take a look today/tomorrow. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything up to "Move FunctionNamespaceManager to SPI" LGTM
/** | ||
* Ideally function namespaces should support transactions like connectors do, and getCandidates should be transaction-aware. | ||
* queryId serves as a transaction ID before proper support for transaction is introduced. | ||
* TODO Support transaction in function namespaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might need to add @Experimental
for this interface
* for a given queryId, getFunctionHandle with the same queryId should return a valid FunctionHandle, even if the function | ||
* is deleted. Multiple calls of this function with the same parameters should return the same FunctionHandle. | ||
* queryId serves as a transaction ID before proper support for transaction is introduced. | ||
* TODO Support transaction in function namespaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might need to add @Experimental
for this interface
.map(TypeSignatureProvider::getTypeSignature) | ||
.collect(toImmutableList())); | ||
} | ||
else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
else is redundant
private final List<String> originalParts; | ||
|
||
@JsonCreator | ||
public static FullyQualifiedName of(String dottedName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method has different assumption for the parameter from other overloaded of
method. Should we give it a different name to avoid confusion?
It is also less readable this way since both this method and the next method have the same name while both can take one String parameter as their inputs.
return of(asList(parts)); | ||
} | ||
|
||
public static FullyQualifiedName of(String... parts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Supplying 2 parts always throw. Instead we can do
public static FullyQualifiedName of(String first, String second, String third, String... rest)
return new FullyQualifiedName(originalParts, parts); | ||
} | ||
|
||
public static FullyQualifiedName of(FullyQualifiedName.Prefix prefix, String name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be only use case of Prefix
. Should we add a similar constraints to Prefix
to only allow 2 or more parts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I added that in later commits. But here since the constructor is private I don't see the need.
352533c
to
d8b28d0
Compare
public StaticFunctionNamespaceStore(FunctionManager functionManager, StaticFunctionNamespaceStoreConfig config) | ||
{ | ||
this.functionManager = functionManager; | ||
this.configDir = config.getFunctionNamespaceConfigurationDir(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this allowed to be null? If so, use Optional
instead.
|
||
private static List<File> listFiles(File dir) | ||
{ | ||
if (dir != null && dir.isDirectory()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: return empty list early:
if (...) {
return ImmutableList.of();
}
...
import static com.facebook.presto.util.PropertiesUtil.loadProperties; | ||
import static com.google.common.base.Preconditions.checkState; | ||
|
||
public class StaticFunctionNamespaceStore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is called StaticFunctionNamespaceStore
so I expect it to be relevant only to the built-in functions and StiatcFunctionNamespaceManager
, but instead, this class serves as an entry point to load all the function namespace managers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation of the StaticFunctionNamespaceStore
is more or less a copy of StaticCatalogStore
. Here "static" is more referring to the available function namespaces and how they are served are configured statically. I'm open to name suggestions. Maybe we can change StaticFunctionNamespace
to BuiltinFunctionNamespace
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me. I think that would avoid confusion from overloading the meaning of static
lgtm overall, but since I'm not yet familiar with the code, I'll let @highker approve. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One high-level comment regarding config
createTestingViewCodec(), | ||
blockEncodingSerde, | ||
sessionPropertyManager, | ||
schemaPropertyManager, | ||
tablePropertyManager, | ||
columnPropertyManager, | ||
analyzePropertyManager, | ||
transactionManager); | ||
transactionManager, | ||
new FunctionManager(typeManager, blockEncodingSerde, featuresConfig)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FunctionManager
can be injected right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constructor is used for testing purposes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you annotate the constructor of MetadataManager
with for test only as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is called in LocalQueryRunner
which is used for testing but is not in the /test
directory. Also it's not about visibility, so not sure whether @VisibleForTesting
is appropriate. Is there other annotations for testing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That should be fine. XXXQueryRunner
is for testing only (as a convention) anyway....
private static final Splitter SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); | ||
|
||
private final FunctionManager functionManager; | ||
private final File configDir; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need a directory? We have been gradually ditching file-based configs. The only place I remember we still use is the catalog directory that is used for installing connector plugins and the other file-based configs are legacy logic. If the config is just some attributes, shall we use the normal configuration than a file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm basically copying the configuration of catalogs here because the concepts kinda overlap so I think it's nice to keep them the same way. Happy to take other alternatives if they makes more sense. What do you mean by "normal configuration"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I should understand more about how function namespaces will be configured. Will it be something similar to connectors, where each connector has its own config? Or it will be something simple like log.properties
below. The former may need a file-based config but the latter can be just put into a new class like LogConfig.java
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way I see this, each config would be configuring a different function namespace manager, which can manage 1 or many function namespaces. Some function namespaces are just logical separations but physically could be managed the same way. For example, you might want to have separate function namespaces for different teams, but they are all SQL functions and can be managed using the same manager with the same configuration. On the other hand, other function namespaces can be physically different. For example, you may want to support MySQL functions as a different function namespace, so people can directly use mysql.function.foo
in their queries. This kind of function namespaces would have a 1:1 mapping to the connector they serve.
Let me know whether you think this is reasonable. We can discuss this further.
@@ -400,7 +400,7 @@ protected void setup(Binder binder) | |||
// connector plan optimizer manager | |||
binder.bind(ConnectorPlanOptimizerManager.class).in(Scopes.SINGLETON); | |||
|
|||
// index manager | |||
// index managerTestVariableExtractor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like an accidental change
d8b28d0
to
ced4deb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
ced4deb
to
7792381
Compare
@highker I added an additional commit to rename |
7792381
to
df42c75
Compare
StaticFunctionNamespace will be handled separately. It won't need to be dynamically registered nor will it be used to support dynamic namespaces so the factory is not necessary.
Break the resolveFunction API into getCandidates and getFunctionHandle so the function resolution logic can be managed consistently within the query engine.
We enforce explicit naming for dynamic function namespaces. All unqualified function names will only be resolved against the built-in static function namespace. While it is possible to define an ordering (through SQL path or other means) and convention (best match / first match), in reality when complicated namespaces are involved such implicit resolution might hide errors and cause confusion.
df42c75
to
3379b4f
Compare
No description provided.