Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
558 changes: 548 additions & 10 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ members = [
"benchmarks/datafusion-bench",
"benchmarks/duckdb-bench",
"benchmarks/random-access-bench",
"vortex-sqllogictest",
]
exclude = ["java/testfiles", "wasm-test"]
resolver = "2"
Expand Down Expand Up @@ -133,6 +134,7 @@ datafusion-physical-expr-adapter = { version = "52" }
datafusion-physical-expr-common = { version = "52" }
datafusion-physical-plan = { version = "52" }
datafusion-pruning = { version = "52" }
datafusion-sqllogictest = { version = "52" }
dirs = "6.0.0"
divan = { package = "codspeed-divan-compat", version = "4.0.4" }
enum-iterator = "2.0.0"
Expand Down
10 changes: 8 additions & 2 deletions vortex-duckdb/src/duckdb/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,14 @@ mod tests {
.query("SELECT 1 as int_col, 'text' as str_col")
.unwrap();

assert_eq!(result.column_type(0), cpp::DUCKDB_TYPE::DUCKDB_TYPE_INTEGER);
assert_eq!(result.column_type(1), cpp::DUCKDB_TYPE::DUCKDB_TYPE_VARCHAR);
assert_eq!(
result.column_type(0).as_type_id(),
cpp::DUCKDB_TYPE::DUCKDB_TYPE_INTEGER
);
assert_eq!(
result.column_type(1).as_type_id(),
cpp::DUCKDB_TYPE::DUCKDB_TYPE_VARCHAR
);
}

#[test]
Expand Down
16 changes: 12 additions & 4 deletions vortex-duckdb/src/duckdb/logical_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ impl LogicalType {
Self::new(DUCKDB_TYPE::DUCKDB_TYPE_BLOB)
}

pub fn int64() -> Self {
Self::new(DUCKDB_TYPE::DUCKDB_TYPE_BIGINT)
}

pub fn uint64() -> Self {
Self::new(DUCKDB_TYPE::DUCKDB_TYPE_UBIGINT)
}
Expand All @@ -146,10 +142,22 @@ impl LogicalType {
Self::new(DUCKDB_TYPE::DUCKDB_TYPE_INTEGER)
}

pub fn int64() -> Self {
Self::new(DUCKDB_TYPE::DUCKDB_TYPE_BIGINT)
}

pub fn bool() -> Self {
Self::new(DUCKDB_TYPE::DUCKDB_TYPE_BOOLEAN)
}

pub fn float32() -> Self {
Self::new(DUCKDB_TYPE::DUCKDB_TYPE_FLOAT)
}

pub fn float64() -> Self {
Self::new(DUCKDB_TYPE::DUCKDB_TYPE_DOUBLE)
}

pub fn as_decimal(&self) -> (u8, u8) {
unsafe {
(
Expand Down
6 changes: 4 additions & 2 deletions vortex-duckdb/src/duckdb/query_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use vortex::error::VortexResult;
use vortex::error::vortex_bail;
use vortex::error::vortex_err;

use crate::LogicalType;
use crate::cpp;
use crate::duckdb::DataChunk;
use crate::wrapper;
Expand Down Expand Up @@ -67,8 +68,9 @@ impl QueryResult {
}

/// Get the type of a column by index.
pub fn column_type(&self, col_idx: usize) -> cpp::DUCKDB_TYPE {
unsafe { cpp::duckdb_column_type(self.as_ptr(), col_idx as u64) }
pub fn column_type(&self, col_idx: usize) -> LogicalType {
let dtype = unsafe { cpp::duckdb_column_type(self.as_ptr(), col_idx as u64) };
LogicalType::new(dtype)
}
}

Expand Down
2 changes: 1 addition & 1 deletion vortex-duckdb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub use crate::duckdb::LogicalType;
pub use crate::duckdb::Value;
use crate::scan::VortexTableFunction;

mod convert;
pub mod convert;
pub mod duckdb;
pub mod exporter;
mod scan;
Expand Down
35 changes: 35 additions & 0 deletions vortex-sqllogictest/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "vortex-sqllogictest"
authors = { workspace = true }
description = "Test runner for SQL integrations"
edition = { workspace = true }
homepage = { workspace = true }
include = { workspace = true }
keywords = { workspace = true }
license = { workspace = true }
publish = false
readme = "README.md"
repository = { workspace = true }
rust-version = { workspace = true }
version = { workspace = true }

[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
clap = { workspace = true, features = ["derive"] }
datafusion = { workspace = true }
datafusion-sqllogictest = { workspace = true }
sqllogictest = "0.28"
thiserror = { workspace = true }
tokio = { workspace = true, features = ["full"] }
vortex = { workspace = true }
vortex-datafusion = { workspace = true }
vortex-duckdb = { workspace = true }

[lints]
workspace = true

[[test]]
harness = false
name = "sqllogictests"
path = "bin/sqllogictests-runner.rs"
14 changes: 14 additions & 0 deletions vortex-sqllogictest/bin/sqllogictests-runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors

use clap::Parser;
use vortex_sqllogictest::args::Args;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
println!("Hello, world!");
println!("Args: {args:?}");

Ok(())
}
20 changes: 20 additions & 0 deletions vortex-sqllogictest/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors

use clap::Parser;

#[derive(clap::ValueEnum, Clone, Copy, Debug)]
pub enum Engine {
#[clap(name = "datafusion")]
DataFusion,
#[clap(name = "duckdb")]
DuckDB,
}

#[derive(Parser, Debug)]
pub struct Args {
#[arg(short, long, value_enum, value_delimiter = ',')]
engine: Option<Vec<Engine>>,
#[arg(action)]
filter: Option<String>,
}
99 changes: 99 additions & 0 deletions vortex-sqllogictest/src/duckdb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors

use std::sync::Arc;
use std::{process::Command, time::Duration};

use async_trait::async_trait;
use datafusion_sqllogictest::DFColumnType;
use sqllogictest::{DBOutput, runner::AsyncDB};

use vortex_duckdb::LogicalType;
use vortex_duckdb::duckdb::Connection;
use vortex_duckdb::duckdb::Database;
use vortex_duckdb::duckdb::{Config, DuckDBType};

use crate::error::TestError;

struct Inner {
conn: Connection,
db: Database,
}

unsafe impl Send for Inner {}
unsafe impl Sync for Inner {}

struct DuckDB {
inner: Arc<Inner>,
}

impl DuckDB {
fn normalize_column_type(dtype: LogicalType) -> DFColumnType {
let type_id = dtype.as_type_id();
if type_id == LogicalType::int32().as_type_id()
|| type_id == LogicalType::int64().as_type_id()
|| type_id == LogicalType::uint64().as_type_id()
{
DFColumnType::Integer
} else if type_id == LogicalType::varchar().as_type_id() {
DFColumnType::Text
} else if type_id == LogicalType::bool().as_type_id() {
DFColumnType::Boolean
} else if type_id == LogicalType::float32().as_type_id()
|| type_id == LogicalType::float64().as_type_id()
{
DFColumnType::Float
} else {
DFColumnType::Another
}
}
}

#[async_trait]
impl AsyncDB for DuckDB {
type Error = TestError;
type ColumnType = DFColumnType;

async fn run(&mut self, sql: &str) -> Result<DBOutput<Self::ColumnType>, Self::Error> {
let r = self
.inner
.conn
.query(sql)
.map_err(|e| TestError::Other(e.to_string()))?;

if r.column_count() == 0 && r.row_count() == 0 {
Ok(DBOutput::StatementComplete(0))
} else {
let mut types = Vec::default();
let rows = Vec::default();

for col_idx in 0..r.column_count() as usize {
let dtype = r.column_type(col_idx);
types.push(Self::normalize_column_type(dtype));
}

Ok(DBOutput::Rows { types, rows })
}
}

async fn shutdown(&mut self) {
todo!()
}

fn engine_name(&self) -> &str {
"DuckDB"
}

async fn sleep(dur: Duration) {
tokio::time::sleep(dur).await
}

/// [`Runner`] calls this function to run a system command.
///
/// The default implementation is `std::process::Command::output`, which is universal to any
/// async runtime but would block the current thread. If you are running in tokio runtime, you
/// should override this by `tokio::process::Command::output`.
async fn run_command(command: Command) -> std::io::Result<std::process::Output> {
tokio::process::Command::from(command).output().await
}
}
29 changes: 29 additions & 0 deletions vortex-sqllogictest/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors

use datafusion_sqllogictest::DFSqlLogicTestError;

#[derive(Debug, thiserror::Error)]
pub enum TestError {
Other(String),
}

impl std::fmt::Display for TestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TestError::Other(msg) => write!(f, "Other: {msg}"),
}
}
}

impl From<DFSqlLogicTestError> for TestError {
fn from(value: DFSqlLogicTestError) -> Self {
match value {
DFSqlLogicTestError::SqlLogicTest(test_error) => todo!(),
DFSqlLogicTestError::DataFusion(data_fusion_error) => todo!(),
DFSqlLogicTestError::Sql(parser_error) => todo!(),
DFSqlLogicTestError::Arrow(arrow_error) => todo!(),
DFSqlLogicTestError::Other(_) => todo!(),
}
}
}
6 changes: 6 additions & 0 deletions vortex-sqllogictest/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors

pub mod args;
pub mod duckdb;
pub mod error;
Loading