Ripley Lorrin created IGNITE-29048:
--------------------------------------
Summary: Preserve column aliases during dynamic schema updates
Key: IGNITE-29048
URL: https://issues.apache.org/jira/browse/IGNITE-29048
Project: Ignite
Issue Type: Bug
Components: cache
Affects Versions: 2.18, 2.17
Reporter: Ripley Lorrin
Fix For: 2.19
When a Cache is starting, it is possible to configure an Alias to the SQL
Columns in order to perform queries using this alias.
However, in the event of a dynamic schema update using
{{GridQueryProcessor#dynamicColumnAdd}} or
{{{}GridQueryProcessor#dynamicColumnRemove{}}}, the underlying called method
{{GridQueryProcessor#processDynamicAddColumn}} omits the "alias" argument while
adding a {{{}QueryBinaryProperty{}}}. (it is always set to null).
This results in an impossibility to properly set an alias during a dynamic
schema change, thus a system that relies on aliases for queries will experience
failures for dynamically added fields.
The problem affects not only a dynamic column addition, but also the dynamic
drop by causality: if a system uses aliases to dynamically delete a dynamically
added column (that does not save their alias in the
{{{}QueryBinaryProperty{}}}), Ignite will rightly say that the column does not
exist.
Here is my proposed solution to allow the preservation of column aliases during
dynamic schema updates - it consists of two one-liners.
h2. *{{GridQueryProcessor (ignite-core)}}*
{color:#4c9aff}*Current snippet*{color}
{code:java}
private void processDynamicAddColumn(QueryTypeDescriptorImpl d,
List<QueryField> cols)
throws IgniteCheckedException {
List<GridQueryProperty> props = new ArrayList<>(cols.size());
for (QueryField col : cols) {
try {
props.add(new QueryBinaryProperty(
ctx,
col.name(),
null,
Class.forName(col.typeName()),
false,
null, // <-- this is where the alias should be injected
!col.isNullable(),
null,
col.precision(),
col.scale()));
}
catch (ClassNotFoundException e) {
throw new SchemaOperationException("Class not found for new
property: " + col.typeName());
}
} for (GridQueryProperty p : props)
d.addProperty(p, true);
} {code}
*{color:#00875a}Proposed snippet{color}*
{code:java}
for (QueryField col : cols) {
try {
props.add(new QueryBinaryProperty(
ctx,
col.name(),
null,
Class.forName(col.typeName()),
false,
col.alias(),
!col.isNullable(),
null,
col.precision(),
col.scale()));
}
catch (ClassNotFoundException e) {
throw new SchemaOperationException("Class not found for new property: "
+ col.typeName());
}
}{code}
----------------
An edition should be done in ignite-indexing's {{GridH2Table}} to take into
account that the column alias should be taken instead of the name if present.
h2. {{*GridH2Table (ignite-indexing)*}}
{color:#4c9aff}Current snippet{color}
{code:java}
// And now, let's add new columns
for (QueryField col : cols) {
if (doesColumnExist(col.name()))
return;
try {
Column c = new Column(col.name(),
DataType.getTypeFromClass(Class.forName(col.typeName())));
c.setNullable(col.isNullable());
newCols[pos++] = c;
}
catch (ClassNotFoundException e) {
throw new IgniteSQLException("H2 data type not found for class: " +
col.typeName(), e);
}
}{code}
{color:#00875a}Proposed snippet{color}
{code:java}
// And now, let's add new columns
for (QueryField col : cols) {
if (doesColumnExist(col.alias()))
return;
try {
Column c = new Column(col.alias(),
DataType.getTypeFromClass(Class.forName(col.typeName())));
c.setNullable(col.isNullable());
newCols[pos++] = c;
}
catch (ClassNotFoundException e) {
throw new IgniteSQLException("H2 data type not found for class: " +
col.typeName(), e);
}
} {code}
Replacing the name with the alias does not pose an issue for systems that rely
on the 'name', because {{QueryFields}} conveniently set the name as the alias
as a fallback.
{code:java}
/**
* @return Field alias.
*/
public String alias() {
return alias != null ? alias : name;
}{code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)