-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
(#4462) Postgres compatibility tests using sqllogictest #4834
(#4462) Postgres compatibility tests using sqllogictest #4834
Conversation
datafusion/core/tests/sqllogictests/postgres/test_files/simple_aggregation.slt
Outdated
Show resolved
Hide resolved
...n/core/tests/sqllogictests/postgres/test_files/simple_window_partition_order_aggregation.slt
Outdated
Show resolved
Hide resolved
datafusion/core/tests/sqllogictests/postgres/test_files/values_list.slt
Outdated
Show resolved
Hide resolved
) | ||
} | ||
|
||
pub async fn connect_with_retry( |
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.
In my implementation a docker container starts before each test. And sometimes first connections fail. I didn't find a solution to deterministically define when a container is fully ready, so I introduced a connection retry.
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 in general the approach of orchestrating the docker containers using github CI worked well rather than restarting the containers within the tests
@@ -111,23 +111,23 @@ fn register_median_test_tables(ctx: &SessionContext) { | |||
} | |||
} | |||
|
|||
async fn register_aggregate_csv_by_sql(ctx: &SessionContext) { | |||
pub async fn register_aggregate_csv_by_sql(ctx: &SessionContext) { |
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 re-used the existing table creation function and updated the data types according to the sql from integration-tests
https://github.com/apache/arrow-datafusion/blob/master/integration-tests/create_test_table.sql.
I can, of course, introduce another function.
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.
Thank you @melgenek -- this is great progress
datafusion/core/tests/sqllogictests/postgres/test_files/simple_aggregation.slt
Outdated
Show resolved
Hide resolved
datafusion/core/tests/sqllogictests/postgres/test_files/self_join_with_alias.slt
Outdated
Show resolved
Hide resolved
datafusion/core/tests/sqllogictests/postgres/test_files/simple_except.slt
Outdated
Show resolved
Hide resolved
Merge slt files
datafusion/core/Cargo.toml
Outdated
half = "2.2.1" | ||
parquet-test-utils = { path = "../../parquet-test-utils" } | ||
postgres-types = { version = "0.2.4", features = ["derive", "with-chrono-0_4"] } | ||
rstest = "0.16.0" | ||
rust_decimal = { version = "1.27.0", features = ["tokio-pg"] } | ||
sqllogictest = "0.10.0" | ||
test-utils = { path = "../../test-utils" } | ||
testcontainers = "0.14.0" | ||
thiserror = "1.0.37" | ||
|
||
tokio-postgres = "0.7.7" |
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.
Here are the reasons for these dependencies:
half
-f16
type for Datafusiontestcontainers
- creates a fresh docker container with Postgres for each sqllogictest file.postgres-types
andtokio-postgres
- these are required for writing a Postgres clientrust_decimal
- converts Postgres "numeric" type to a rust typebigdecimal
- provides a common type to do floating number rounding.rust_decimal
, unfortunately, doesn't handle numbers of arbitrary precision. For example,rust_decimal
could not parse26156334342021890000000000000000000000
that is currently present in one of.slt
tests in Datafusion.
} | ||
|
||
pub fn big_decimal_to_str(value: BigDecimal) -> String { | ||
value.round(12).normalized().to_string() |
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.
All numbers are rounded to 12
decimal digits. Without explicit types Postgres and Datafusion can choose different underlying types. For example, Postgres could choose to use numeric
when Datafusion uses int
. In order to compare results, all floating number types are converted to the same number of decimal points.
12
is chosen to pass the existing set of tests. I think it could produce errors, for example, when rounding f16
to 12 digits. I would probably use 3
(or 4
) decimal digits if high precision is not required for Postgres compatibility tests. 3
or 4
is an expected number of digits for 16 bit binary according to IEEE_754, so it should be safer to round to the smallest possible data type.
if !col.is_valid(row) { | ||
// represent any null value with the string "NULL" | ||
Ok(NULL_STR.to_string()) | ||
} else if is_pg_compatibility_test { |
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.
If a test is a Postgres compatibility test then some conversions apply. The same conversions are used in the Postgres client implementation.
This is needed, because existing sqllogictests use higher precision of floating number results, which would produce different results in Postgres.
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 am not sure the super high precision floating point results are necessary. I wonder if we could simply use the lower precision normalization in all the tests (both in pgcompat and non pg compat) 🤔
}}; | ||
} | ||
|
||
fn cell_to_string(row: &Row, column: &Column, idx: usize) -> String { |
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 Postgres client is based on the "postgres-extended" from the sqllogictest-rs repo https://github.com/risinglightdb/sqllogictest-rs/blob/2fb7e36e1857fd6b7949956b496c26ddc463f858/sqllogictest-bin/src/engines/postgres_extended.rs.
All the type conversions are now through rust like in the Datafusion. So every type has to be converted to a string explicitly.
I implemented the types marked as implemented from https://github.com/apache/arrow-datafusion/blob/master/docs/source/user-guide/sql/data_types.md and made their text representation compatible with Datafusion.
I haven't implemented arrays.
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 if options.postgres_runner { | ||
if is_pg_compatibility_test { | ||
run_test_file_with_postgres(&path, file_name).await?; | ||
} else { | ||
debug!("Skipping test file {:?}", path); | ||
} |
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 tests which name starts with pg_compat_
would run with Datafusion during cargo test
.
In order to check compatibility with Postgres, there is a github job that runs these tests with a Postgres runner.
Thus Datafusion runs all the time. And Postgres runs only explicitly. This way cargo test
is not affected by the speed of running docker containers and executing Postgres queries.
@alamb The pr is updated according to your previous review:
|
Thanks @melgenek -- this PR is on my list to review -- but I ran out of time today. I will plan to do so 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.
I really like this @melgenek -- Thank you, I have several changes I would like to make, but I also think we could make them as (fast) follow on PRs. Specifically:
- Don't orchestrate the postgres containers with rust test code, rather orchestrate outside (either with an existing container or github CI, and document what is needed). Making this change will avoid requiring docker to run the datafusion tests,
- Use the same normalization in postgres and non-postgres code (maybe by decreasing fidelity of datafusion test)
- Remove existing postgres python integration tests
Given how large this PR is already I would be inclined to merge it as is and iterate as a follow on. What do you think @xudong963 @jimexist @avantgardnerio ?
if !col.is_valid(row) { | ||
// represent any null value with the string "NULL" | ||
Ok(NULL_STR.to_string()) | ||
} else if is_pg_compatibility_test { |
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 am not sure the super high precision floating point results are necessary. I wonder if we could simply use the lower precision normalization in all the tests (both in pgcompat and non pg compat) 🤔
) | ||
} | ||
|
||
pub async fn connect_with_retry( |
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 in general the approach of orchestrating the docker containers using github CI worked well rather than restarting the containers within the tests
}}; | ||
} | ||
|
||
fn cell_to_string(row: &Row, column: &Column, idx: usize) -> String { |
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.
👍
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.
Good catch... I 💯 % agree with this. |
🤔 it seems like risinglightdb/sqllogictest-rs#154 may be related to this PR (they have factored out their own postgres driver) I think I can make time to help this PR along this weekend if that would assist. |
This sounds reasonable to me. I'll try to make prs for points 1 and 2 in the next few days.
I saw this change, but I don't think that extraction is currently sufficient to fully replace implementation in this pr. The biggest part of the Postgres client is the results conversion. And if it is made pluggable into the client from |
Thanks @melgenek . I filed issues for the follow on tasks: |
Assuming this PR passes CI checks I plan to merge |
merged, let's iterate! |
Benchmark runs are scheduled for baseline = b71cae8 and contender = 1d69f28. 1d69f28 is a master commit associated with this PR. Results will be available as each benchmark for each run completes. |
Thanks again @melgenek and @xudong963 -- great to see this pushed along |
Which issue does this PR close?
Closes #4462.
Rationale for this change
It is an example implementation of the
sqllogictest
tests that run on both Postgres and Datafusion and compare results.What changes are included in this PR?
sqllogictest
to
sqllogictest
formatAre these changes tested?
There is a dedicated Github action that runs Postgres sqllogictest tests.
Are there any user-facing changes?
N/A