RFC-0025 Derived column - #61
Conversation
474cf06 to
221e8c0
Compare
221e8c0 to
8690c4e
Compare
3bdd1ef to
2448a60
Compare
2448a60 to
8c6c4a4
Compare
jja725
left a comment
There was a problem hiding this comment.
Agree that how write work would be the main concern here with compatibility with all the engine
8c6c4a4 to
03935be
Compare
|
@tdcmeehan has volunteered to be a co-author ! Yay! |
03935be to
6629e4e
Compare
82feae1 to
334810b
Compare
| { | ||
| "udfSpecList" : [ { | ||
| "derivedColumnType" : "PERSISTENT", | ||
| "derivedColumnExpression" : "SQL expression", |
There was a problem hiding this comment.
Can you give more info about the SQL dialect of this expression ? Seems like you want atleast Presto and Spark to understand it.
There was a problem hiding this comment.
To be clear, deriving a common subset of expressions that are interpretable by both Spark and Presto is hard and likely outside of the scope of this RFC. I think the most straightforward thing is to treat them like views, which defer on cross-platform interpretability and force any consumer of the view SQL to understand Presto's dialect. Cross platform expressions can be considered an orthogonal yet important task.
7b6ee54 to
73e85c3
Compare
73e85c3 to
5d03e79
Compare
|
Hi Folks @tdcmeehan , @aditi-pandit, @aaneja and @agrawalreetika Can you please give me a review feedback ! |
2a554a4 to
422e369
Compare
|
prestodb/presto#28123 is the PR with just parser and storing table properties in iceberg connector. POC PR: prestodb/presto#27832 |
hantangwangd
left a comment
There was a problem hiding this comment.
Hi @ScrapCodes, thanks for this RFC. Regarding our eventual perspective on derived columns, I have some high-level questions and thoughts for discussion:
- When adding a derived column to an existing table that already contains data, how are the values for existing rows handled?
- When inserting rows, is the derived column: (a) not specifiable, (b) specifiable but auto-computed if omitted, or (c) mandatory (NULL when absent)?
- For the tracking and detection of sync states of derived columns:
- Is the engine responsible for tracking and detection? If so, how would that be implemented? (Considering that other unaware engines might perform writes, source columns could be updated, or even the function logic itself could change, etc.)
- Or, the engine wouldn't manage the sync state—users are responsible for their own data. The engine would simply provide sync capability for users to call on demand, similar to
REFRESH MVorREWRITE_DATA_FILES.
- Filter and projection rewrite optimizations are indeed a huge performance advantage that derived columns can bring. But VIRTUAL-mode derived columns don't seem to offer that benefit—so what's the value of having them?
Ultimately, the core question is: in our final perspective, is a derived column a special type of column, or is it essentially more like a normal column with an optimization hint?
Looking at the overall design and the POC examples, it seems we're mainly considering the latter—where derived columns are essentially more like normal columns with an optimization hint. However, many of the concerns outlined in the "Considerations" and "Challenges" sections appear to stem from the former.
In my view, the gap between these two modes is quite significant. I think it would be helpful to first clearly define the boundaries—specifically, what is feasible and on our roadmap versus what is out of scope or not advisable.
Just some initial thoughts on my side—happy to be corrected if I've misunderstood anything, and looking forward to further discussion.
| 1. We allow direct insert into derived columns. If the table is written by another engine which does not support derived columns, write to derived columns cannot be restricted. | ||
| 2. It is thus responsibility of the user to maintain derived column information as up to date. Any stale entry in derived column will lead optimizer rewrite to produce incorrect | ||
| result when the derived column feature flags are enabled. | ||
| 3. We can provide warning, the derived column data is out of sync. And then it is user's responsibility they can drop and recreate the column. (Future work). |
There was a problem hiding this comment.
Just curious: how do we track and detect that a derived column is out of sync? And can we simply drop and re-add the derived column to sync it?
There was a problem hiding this comment.
Yes, a drop and re-add the derived column will trigger the expression to be applied on existing rows and new column updated.
In the absence of INSERT support for derived column we will have to do UPDATE e.g. UPDATE test2 SET c2_derived = lower(c2).
Hi Dong Wang (@hantangwangd), thanks for the thorough review and your comments are all valid. Here is my attempt to answer, I could be missing some things and hope @tdcmeehan can cover up and correct! @aditi-pandit and @aaneja have also glanced at it and can help ! Some background: Derived columns is a standard feature in RDBMS and has a SQL standard spec, the key challenge here is RDBMS is like a walled garden and they do not have the problem of data may be updated from outside their system in anyway possible. In Presto, this cannot be guaranteed and data may be updated in various ways from outside our system. While dealing with table formats e.g. Iceberg this is specially a concern. And only ultimate solution is a Spec update from Iceberg which enables the derived column as a first class feature. I started working on it, but haven't finished. (Link). In the absence of a first class feature support from Iceberg, we are using table properties to store derived column metadata. Which appears as if derived columns are regular columns with just a optimization hint. However, this is not the intention. Once this feature is fully complete - this will be clearly spelled. The work for derived column is divided into parts, a) The parser and related connector changes to store the derived column metadata and basic validation for checking derived column may be out of sync. b) The query rewrite rule to benefit from derived columns. c) Implementation of Insert/Merge/Update i.e. for Derived columns we provide the option of a column could be GENERATED ALWAYS i.e. user inserts are rejected and the values are always generated. This way derived column is not a normal column with a optimization hint, it is a special column. It is also possible to have derived column without this restriction and user insert is permitted and also auto-generate when requested - the syntax in MariaDb for example looks like d) Once we control the insert, we can track last updated snapshot ID by presto and if this ID does not match with the latest Snapshot ID, we can say there may be a update to the table outside of presto and derived column's data "may have gone" out of sync. e) While we develop this with table properties, we are also planning to move ahead with Iceberg Spec change and presto already embracing derived columns will be a strong case for Iceberg's spec change acceptance. Iceberg spec change can take much longer time. The current RFC only covers a) and b), leaves the details of c, d, e in future work section. I took this route, so that we can evolve the RFC (hoping that RFCs can evolve), i.e. develop in stages and we do not have to have everything flushed out at once. I am open to be corrected on my approach overall or even the part that we should have all the details of fully developed Feature before we can begin.
With INSERT support not ready, this is done manually using UPDATE followed by adding a derived column. Same is true of adding rows to the table, INSERT to derived column are done manually. Once the INSERT support is added, this step will be automated. i.e. Derived column will run an update with the expression for all the existing rows in the table. Therefore dropping and recreating the column will have the effect of syncing the derived column.
Specifiable,
[above excerpt is taken from MariaDb docs, link in RFC]
These details are not updated in the current RFC as they are future work.
True for detecting function logic change we do not have anything, because built-in functions in Presto are not versioned and a plugin can add built-in functions and change without a way for us to know. For Tableformats like Iceberg, engine is responsible for tracking and detection. And the way would be to track snapshot ID for any updates made to the table. This way we can tell "may be there was a change" and table is out of sync due to an external update. But we cannot tell for sure, there is a need to sync the derived column.
That is correct !
From Iceberg connector perspective, VIRTUAL mode in derived column will need a support from ICEBERG spec. They will help us in improving data-file pruning only ! For example they will maintain only column metadata/metrics i.e. lower/upper bounds or bloom filters in Iceberg manifests/puffin files. When they are used in filters, this metadata will help us prune the data files. The performance boast will come when those expressions cannot be pushed down, however the virtual column representing them can be pushed and data files pruned.
You are right, the gap between two modes is quite significant, and derived column is a special column all RDBMS system treat it as a special column. For ICEBERG table format, we have gone the table properties route which is what I think you alluding to as normal column with a optimizer hint. But, that is not what we want ultimately, we want to have derived column as a special column - with support defined in Iceberg Spec.
Thanks again for these questions! |
422e369 to
5ae0f6b
Compare
5ae0f6b to
cc8531d
Compare
|
Hi @hantangwangd ,
WDYT ? cc @tdcmeehan / @aditi-pandit / @aaneja |
|
@ScrapCodes, thanks for your detailed answer. A few thoughts below—happy to discuss. When we consider only (a) and (b), or in other words, when we treat a derived column more like a normal column plus optimization hint, then:
Based on the above, the derived column feature at this stage appears to be fully compatible with the underlying table format and across engines, and there seem to be no blockers to supporting and implementing it at this stage. Regarding the latter points (c), (d), (e), I have given them some thought. Please correct me if I have misunderstood anything. When the Presto engine begins to support derived columns that do not allow user specification and can only be computed automatically—as discussed earlier—such a column appears to the engine as an entirely new type of column: one whose representation in the table schema is inconsistent between reads and writes. For example, consider the following statements: One idea is to treat derived columns as metadata columns, much like I fully agree with the steps you are taking: first, support features that are compatible with the table format specification and do not break cross-engine interoperability; in parallel, drive the proposal-level change to the Iceberg specification; and finally, once the spec change is adopted and finalized, revisit the Presto implementation to support the full-featured, spec-aligned version of the feature. One thing that still puzzles me, however, is how exactly this derived column should be positioned—what type of column it should be, even within the Iceberg specification. That is, even if Iceberg spec were to support derived columns, it would still need to address similar issues to those discussed above. I would be very interested to hear your thoughts on this. |
What is a derived column?
A column created by applying a SQL expression or a UDF to an existing column in a table.
Why do we need that, since we can always apply a UDF to a column during project, filter or join?
Indeed, a derived column consumes O(N) storage, where N is the number of rows in the table. We still need them because, the performance benefits outweigh the disadvantage of extra storage it consumes. Let us understand with the following use case example:
A compute engine like Presto can easily push down a filter predicate e.g. SELECT col1, col2, FROM table T1 WHERE col1='constant_value' , this allows for pruning the number of rows required for TableScan by applying the filtering WHERE col1=’constant_value’. This is not true of when a UDF is involved in the filter predicate, let us take an example SELECT col1, col2, FROM table T1 WHERE lower(col1)='constant_value'. While optimizers can easily push down the filter predicate, however, it can not be used in filtering using the lower and upper bound metrics, for example Iceberg manifest statics and Parquet row group statistics. As a result, we end up scanning a large number of rows.
So, to support push down of certain predicates (with UDFs in them) and reduce the amount of data scanned, derived column bring massive performance improvements. Derived columns have already been proven in RDBMS system e.g. DB2 [1], and now we intend to bring them to Presto.