From 232badbfc718735f5841072f41c459aa8e47f7e4 Mon Sep 17 00:00:00 2001 From: Weijun-H Date: Sun, 13 Oct 2024 14:43:18 +0800 Subject: [PATCH] feat: Integrate sqllogoctest --- Cargo.lock | 4 +- tests/explain.rs | 14 -- tests/sqllogictests/Cargo.toml | 8 +- tests/sqllogictests/bin/sqllogictests.rs | 34 +++-- tests/sqllogictests/src/conversion.rs | 70 +++++++++ tests/sqllogictests/src/engine.rs | 38 +++-- tests/sqllogictests/src/error.rs | 18 +-- tests/sqllogictests/src/lib.rs | 2 + tests/sqllogictests/src/normalize.rs | 105 +++++++++++-- tests/sqllogictests/src/output.rs | 14 -- tests/sqllogictests/test_files/demo.slt | 8 - tests/sqllogictests/test_files/explain.slt | 163 +++++++++++++++++++++ 12 files changed, 379 insertions(+), 99 deletions(-) create mode 100644 tests/sqllogictests/src/conversion.rs delete mode 100644 tests/sqllogictests/test_files/demo.slt create mode 100644 tests/sqllogictests/test_files/explain.slt diff --git a/Cargo.lock b/Cargo.lock index 989c6be8..d2652b8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4910,9 +4910,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ "arrayvec", "borsh", diff --git a/tests/explain.rs b/tests/explain.rs index 16defb59..4a478d5d 100644 --- a/tests/explain.rs +++ b/tests/explain.rs @@ -56,20 +56,6 @@ async fn test_explain_fdw(#[future(awt)] s3: S3, mut conn: PgConnection) -> Resu Ok(()) } -#[rstest] -async fn test_explain_heap(mut conn: PgConnection) -> Result<()> { - NycTripsTable::setup().execute(&mut conn); - - let explain: Vec<(String,)> = - "EXPLAIN SELECT COUNT(*) FROM nyc_trips WHERE tip_amount <> 0".fetch(&mut conn); - - assert!(explain[0].0.contains("Aggregate")); - assert!(explain[1].0.contains("Seq Scan on nyc_trips")); - assert!(explain[2].0.contains("Filter")); - - Ok(()) -} - #[rstest] #[ignore = "EXPLAIN not fully working"] async fn test_explain_federated(#[future(awt)] s3: S3, mut conn: PgConnection) -> Result<()> { diff --git a/tests/sqllogictests/Cargo.toml b/tests/sqllogictests/Cargo.toml index e8e5911a..82aedd88 100644 --- a/tests/sqllogictests/Cargo.toml +++ b/tests/sqllogictests/Cargo.toml @@ -6,8 +6,13 @@ license = "AGPL-3.0" [features] default = ["pg17"] +pg13 = ["pgrx/pg13"] +pg14 = ["pgrx/pg14"] +pg15 = ["pgrx/pg15"] +pg16 = ["pgrx/pg16"] pg17 = ["pgrx/pg17"] + [lib] name = "paradedb_sqllogictest" @@ -28,8 +33,7 @@ num_cpus = "1.13.0" sqlparser = { workspace = true } async-trait = "0.1.83" - -[[bin]] +[[test]] harness = false name = "sqllogictests" path = "bin/sqllogictests.rs" diff --git a/tests/sqllogictests/bin/sqllogictests.rs b/tests/sqllogictests/bin/sqllogictests.rs index 8f3d775b..6d0f45e0 100644 --- a/tests/sqllogictests/bin/sqllogictests.rs +++ b/tests/sqllogictests/bin/sqllogictests.rs @@ -1,3 +1,20 @@ +// Copyright (c) 2023-2024 Retake, Inc. +// +// This file is part of ParadeDB - Postgres for Search and Analytics +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + use std::{ ffi::OsStr, fs, @@ -5,7 +22,7 @@ use std::{ }; use anyhow::Result; -use async_std::task::{self, block_on}; +use async_std::task::block_on; use datafusion::common::runtime::SpawnedTask; use futures::StreamExt; use log::info; @@ -20,17 +37,7 @@ pub fn main() -> Result<()> { } async fn run_tests() -> Result<()> { - // Enable logging (e.g. set RUST_LOG=debug to see debug logs) - // env_logger::init(); - - // let options: Options = clap::Parser::parse(); - // options.warn_on_ignored(); - // Run all tests in parallel, reporting failures at the end - // - // Doing so is safe because each slt file runs with its own - // `SessionContext` and should not have side effects (like - // modifying shared state like `/tmp/`) let errors: Vec<_> = futures::stream::iter(read_test_files()?) .map(|test_file| { SpawnedTask::spawn(async move { @@ -110,11 +117,6 @@ async fn run_test_file(test_file: TestFile) -> Result<()> { } = test_file; info!("Running with ParadeDB runner: {}", path.display()); - // let Some(test_ctx) = TestContext::try_new_for_test_file(&relative_path).await else { - // info!("Skipping: {}", path.display()); - // return Ok(()); - // }; - setup_scratch_dir(&relative_path)?; let mut runner = sqllogictest::Runner::new(|| async { Ok(ParadeDB::new().await) }); diff --git a/tests/sqllogictests/src/conversion.rs b/tests/sqllogictests/src/conversion.rs new file mode 100644 index 00000000..51475638 --- /dev/null +++ b/tests/sqllogictests/src/conversion.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2023-2024 Retake, Inc. +// +// This file is part of ParadeDB - Postgres for Search and Analytics +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use bigdecimal::BigDecimal; +use std::str::FromStr; + +/// Represents a constant for NULL string in your database. +pub const NULL_STR: &str = "NULL"; + +pub(crate) fn bool_to_str(value: bool) -> String { + if value { + "true".to_string() + } else { + "false".to_string() + } +} + +pub(crate) fn varchar_to_str(value: &str) -> String { + if value.is_empty() { + "(empty)".to_string() + } else { + value.trim_end_matches('\n').to_string() + } +} + +pub(crate) fn f32_to_str(value: f32) -> String { + if value.is_nan() { + // The sign of NaN can be different depending on platform. + // So the string representation of NaN ignores the sign. + "NaN".to_string() + } else if value == f32::INFINITY { + "Infinity".to_string() + } else if value == f32::NEG_INFINITY { + "-Infinity".to_string() + } else { + big_decimal_to_str(BigDecimal::from_str(&value.to_string()).unwrap()) + } +} + +pub(crate) fn f64_to_str(value: f64) -> String { + if value.is_nan() { + // The sign of NaN can be different depending on platform. + // So the string representation of NaN ignores the sign. + "NaN".to_string() + } else if value == f64::INFINITY { + "Infinity".to_string() + } else if value == f64::NEG_INFINITY { + "-Infinity".to_string() + } else { + big_decimal_to_str(BigDecimal::from_str(&value.to_string()).unwrap()) + } +} + +pub(crate) fn big_decimal_to_str(value: BigDecimal) -> String { + value.round(12).normalized().to_string() +} diff --git a/tests/sqllogictests/src/engine.rs b/tests/sqllogictests/src/engine.rs index 68f9baba..df87e829 100644 --- a/tests/sqllogictests/src/engine.rs +++ b/tests/sqllogictests/src/engine.rs @@ -19,6 +19,8 @@ // We duplicated because the paradedb repo may use a different version of pgrx than pg_analytics, but eventually we should // move this into a separate crate without any dependencies on pgrx. +use crate::normalize::convert_rows; +use crate::normalize::convert_types; use async_std::prelude::Stream; use async_std::stream::StreamExt; use async_std::task::block_on; @@ -26,6 +28,7 @@ use async_trait::async_trait; use bytes::Bytes; use datafusion::arrow::{datatypes::SchemaRef, record_batch::RecordBatch}; use sqllogictest::DBOutput; +use sqlx::Row; use sqlx::{ postgres::PgRow, testing::{TestArgs, TestContext, TestSupport}, @@ -114,6 +117,13 @@ where }) } + fn fetch_dynamic_result( + self, + connection: &mut PgConnection, + ) -> Result, sqlx::Error> { + block_on(async { sqlx::query(self.as_ref()).fetch_all(connection).await }) + } + /// A convenient helper for processing PgRow results from Postgres into a DataFusion RecordBatch. /// It's important to note that the retrieved RecordBatch may not necessarily have the same /// column order as your Postgres table, or parquet file in a foreign table. @@ -208,11 +218,6 @@ impl sqllogictest::AsyncDB for ParadeDB { type ColumnType = DFColumnType; async fn run(&mut self, sql: &str) -> Result, Self::Error> { - // println!( - // "[{}] Running query: \"{}\"", - // self.relative_path.display(), - // sql - // ); let mut conn = self.connection().await; run_query(sql, &mut conn).await } @@ -223,13 +228,18 @@ impl sqllogictest::AsyncDB for ParadeDB { } async fn run_query(sql: impl Into + Query, conn: &mut PgConnection) -> Result { - let results: Vec = sql.fetch_dynamic(conn); - // let rows = normalize::convert_batches(results)?; - Ok(DBOutput::StatementComplete(0)) - - // if rows.is_empty() && types.is_empty() { - // Ok(DBOutput::StatementComplete(0)) - // } else { - // Ok(DBOutput::Rows { types, rows }) - // } + let results: Vec = sql.fetch_dynamic_result(conn)?; + + let rows = convert_rows(&results); + let types = if rows.is_empty() { + vec![] + } else { + convert_types(results[0].columns()) + }; + + if rows.is_empty() && types.is_empty() { + Ok(DBOutput::StatementComplete(0)) + } else { + Ok(DBOutput::Rows { types, rows }) + } } diff --git a/tests/sqllogictests/src/error.rs b/tests/sqllogictests/src/error.rs index 137508b8..cbaba36a 100644 --- a/tests/sqllogictests/src/error.rs +++ b/tests/sqllogictests/src/error.rs @@ -1,13 +1,3 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// // Copyright (c) 2023-2024 Retake, Inc. // // This file is part of ParadeDB - Postgres for Search and Analytics @@ -25,12 +15,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -// TECH DEBT: This file is a copy of the `db.rs` file from https://github.com/paradedb/paradedb/blob/dev/shared/src/fixtures/db.rs -// We duplicated because the paradedb repo may use a different version of pgrx than pg_analytics, but eventually we should -// move this into a separate crate without any dependencies on pgrx. - use datafusion::arrow::error::ArrowError; -// use datafusion_common::DataFusionError; use sqllogictest::TestError; use sqlparser::parser::ParserError; use thiserror::Error; @@ -40,6 +25,9 @@ pub type Result = std::result::Result; /// DataFusion sql-logicaltest error #[derive(Debug, Error)] pub enum DFSqlLogicTestError { + /// Error from sqlx + #[error("Postgres error(from sqlx crate): {0}")] + Sqlx(#[from] sqlx::Error), /// Error from sqllogictest-rs #[error("SqlLogicTest error(from sqllogictest-rs crate): {0}")] SqlLogicTest(#[from] TestError), diff --git a/tests/sqllogictests/src/lib.rs b/tests/sqllogictests/src/lib.rs index 7819c319..fa96f224 100644 --- a/tests/sqllogictests/src/lib.rs +++ b/tests/sqllogictests/src/lib.rs @@ -1,4 +1,6 @@ pub mod arrow; +mod conversion; pub mod engine; mod error; +mod normalize; mod output; diff --git a/tests/sqllogictests/src/normalize.rs b/tests/sqllogictests/src/normalize.rs index b248758b..cfd614d6 100644 --- a/tests/sqllogictests/src/normalize.rs +++ b/tests/sqllogictests/src/normalize.rs @@ -1,16 +1,93 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at +// Copyright (c) 2023-2024 Retake, Inc. // -// http://www.apache.org/licenses/LICENSE-2.0 +// This file is part of ParadeDB - Postgres for Search and Analytics // -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use crate::conversion::{ + big_decimal_to_str, bool_to_str, f32_to_str, f64_to_str, varchar_to_str, NULL_STR, +}; +use crate::output::DFColumnType; +use bigdecimal::BigDecimal; +use chrono::NaiveDateTime; +use sqlx::postgres::{PgColumn, PgRow}; +use sqlx::TypeInfo; +use sqlx::{Column, Row}; + +pub(crate) fn convert_rows(rows: &[PgRow]) -> Vec> { + rows.iter() + .map(|row| { + row.columns() + .iter() + .enumerate() + .map(|(idx, column)| cell_to_string(row, column, idx)) + .collect::>() + }) + .collect::>() +} + +macro_rules! make_string { + ($row:ident, $idx:ident, $t:ty) => {{ + let value: Option<$t> = $row.get($idx); + match value { + Some(value) => value.to_string(), + None => NULL_STR.to_string(), + } + }}; + ($row:ident, $idx:ident, $t:ty, $convert:ident) => {{ + let value: Option<$t> = $row.get($idx); + match value { + Some(value) => $convert(value).to_string(), + None => NULL_STR.to_string(), + } + }}; +} + +fn cell_to_string(row: &PgRow, column: &PgColumn, idx: usize) -> String { + match column.type_info().name() { + "CHAR" => make_string!(row, idx, i8), + "BOOL" => make_string!(row, idx, bool, bool_to_str), + "INT2" => make_string!(row, idx, i16), + "INT4" => make_string!(row, idx, i32), + "INT8" => make_string!(row, idx, i64), + "FLOAT4" => make_string!(row, idx, f32, f32_to_str), + "FLOAT8" => make_string!(row, idx, f64, f64_to_str), + "NUMERIC" => make_string!(row, idx, BigDecimal, big_decimal_to_str), + "BPCHAR" | "VARCHAR" | "TEXT" => make_string!(row, idx, &str, varchar_to_str), + "DATE" => make_string!(row, idx, chrono::NaiveDate), + "TIME" => make_string!(row, idx, chrono::NaiveTime), + "TIMESTAMP" => { + let value: Option = row.get(idx); + value + .map(|d| format!("{d:?}")) + .unwrap_or_else(|| "NULL".to_string()) + } + name => unimplemented!("Unsupported type: {}", name), + } +} + +pub(crate) fn convert_types(columns: &[PgColumn]) -> Vec { + columns + .iter() + .map(|t| match t.type_info().name() { + "BOOL" => DFColumnType::Boolean, + "INT2" | "INT4" | "INT8" => DFColumnType::Integer, + "BPCHAR" | "VARCHAR" | "TEXT" => DFColumnType::Text, + "FLOAT4" | "FLOAT8" | "NUMERIC" => DFColumnType::Float, + "DATE" | "TIME" => DFColumnType::DateTime, + "TIMESTAMP" => DFColumnType::Timestamp, + _ => DFColumnType::Another, + }) + .collect() +} diff --git a/tests/sqllogictests/src/output.rs b/tests/sqllogictests/src/output.rs index edc9aa77..16b445f1 100644 --- a/tests/sqllogictests/src/output.rs +++ b/tests/sqllogictests/src/output.rs @@ -1,13 +1,3 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// // Copyright (c) 2023-2024 Retake, Inc. // // This file is part of ParadeDB - Postgres for Search and Analytics @@ -25,10 +15,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -// TECH DEBT: This file is a copy of the `db.rs` file from https://github.com/paradedb/paradedb/blob/dev/shared/src/fixtures/db.rs -// We duplicated because the paradedb repo may use a different version of pgrx than pg_analytics, but eventually we should -// move this into a separate crate without any dependencies on pgrx. - use sqllogictest::{ColumnType, DBOutput}; #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/tests/sqllogictests/test_files/demo.slt b/tests/sqllogictests/test_files/demo.slt deleted file mode 100644 index ed86dc8a..00000000 --- a/tests/sqllogictests/test_files/demo.slt +++ /dev/null @@ -1,8 +0,0 @@ -statement ok -CREATE TABLE weather ( - city varchar(80), - temp_lo int, -- low temperature - temp_hi int, -- high temperature - prcp real, -- precipitation - date date -); diff --git a/tests/sqllogictests/test_files/explain.slt b/tests/sqllogictests/test_files/explain.slt new file mode 100644 index 00000000..0178055e --- /dev/null +++ b/tests/sqllogictests/test_files/explain.slt @@ -0,0 +1,163 @@ +# Copyright (c) 2023-2024 Retake, Inc. +# +# This file is part of ParadeDB - Postgres for Search and Analytics +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +####### +# Load the pg_analytics extension +####### +statement ok +DROP EXTENSION IF EXISTS pg_analytics CASCADE; + +statement ok +CREATE EXTENSION pg_analytics; + + +####### +# Setup test data table +####### +statement ok +CREATE TABLE nyc_trips ( + "VendorID" INT, + "tpep_pickup_datetime" TIMESTAMP, + "tpep_dropoff_datetime" TIMESTAMP, + "passenger_count" BIGINT, + "trip_distance" DOUBLE PRECISION, + "RatecodeID" DOUBLE PRECISION, + "store_and_fwd_flag" TEXT, + "PULocationID" REAL, + "DOLocationID" REAL, + "payment_type" DOUBLE PRECISION, + "fare_amount" DOUBLE PRECISION, + "extra" DOUBLE PRECISION, + "mta_tax" DOUBLE PRECISION, + "tip_amount" DOUBLE PRECISION, + "tolls_amount" DOUBLE PRECISION, + "improvement_surcharge" DOUBLE PRECISION, + "total_amount" DOUBLE PRECISION +); + +statement ok +INSERT INTO nyc_trips ("VendorID", tpep_pickup_datetime, tpep_dropoff_datetime, passenger_count, trip_distance, "RatecodeID", store_and_fwd_flag, "PULocationID", "DOLocationID", payment_type, fare_amount, extra, mta_tax, tip_amount, tolls_amount, improvement_surcharge, total_amount) +VALUES +(2, '2024-01-24 15:17:12', '2024-01-24 15:34:53', 1, 3.33, 1, 'N', 239, 246, 1, 20.5, 0, 0.5, 3, 0, 1, 27.5), +(2, '2024-01-24 15:52:24', '2024-01-24 16:01:39', 1, 1.61, 1, 'N', 234, 249, 1, 10.7, 0, 0.5, 3.67, 0, 1, 18.37), +(2, '2024-01-24 15:08:55', '2024-01-24 15:31:35', 1, 4.38, 1, 'N', 88, 211, 1, 25.4, 0, 0.5, 5.88, 0, 1, 35.28), +(2, '2024-01-24 15:42:55', '2024-01-24 15:51:35', 1, 0.95, 1, 'N', 211, 234, 1, 9.3, 0, 0.5, 2.66, 0, 1, 15.96), +(2, '2024-01-24 15:52:23', '2024-01-24 16:12:53', 1, 2.58, 1, 'N', 68, 144, 1, 18.4, 0, 0.5, 4.48, 0, 1, 26.88), +(1, '2024-01-24 15:30:55', '2024-01-24 16:38:46', 1, 15.8, 2, 'N', 164, 132, 1, 70, 2.5, 0.5, 10, 6.94, 1, 90.94), +(2, '2024-01-24 15:21:48', '2024-01-24 15:59:06', 2, 7.69, 1, 'N', 231, 161, 1, 40.8, 0, 0.5, 6.5, 0, 1, 51.3), +(2, '2024-01-24 15:47:59', '2024-01-24 16:12:38', 1, 8.31, 1, 'N', 138, 262, 1, 35.2, 5, 0.5, 10, 6.94, 1, 62.89), +(2, '2024-01-24 15:55:32', '2024-01-24 16:23:01', 1, 8.47, 1, 'N', 132, 192, 2, 36.6, 0, 0.5, 0, 0, 1, 39.85), +(1, '2024-01-24 15:02:22', '2024-01-24 15:13:11', 1, 1.4, 1, 'N', 226, 7, 2, 11.4, 0, 0.5, 0, 0, 1, 12.9), +(1, '2024-01-24 15:49:04', '2024-01-24 15:55:15', 1, 0.9, 1, 'N', 43, 237, 1, 7.9, 5, 0.5, 2.85, 0, 1, 17.25), +(2, '2024-01-24 15:10:53', '2024-01-24 15:20:45', 1, 0.55, 1, 'N', 237, 237, 1, 10, 0, 0.5, 2.8, 0, 1, 16.8), +(1, '2024-01-24 15:09:28', '2024-01-24 16:21:23', 1, 16.2, 5, 'N', 230, 132, 1, 86.55, 0, 0, 17.5, 0, 1, 105.05), +(2, '2024-01-24 15:14:11', '2024-01-24 15:27:17', 1, 0.74, 1, 'N', 236, 237, 2, 12.1, 0, 0.5, 0, 0, 1, 16.1), +(2, '2024-01-24 15:56:34', '2024-01-24 16:27:32', 1, 3.79, 1, 'N', 230, 144, 1, 27.5, 0, 0.5, 7.88, 0, 1, 39.38), +(2, '2024-01-24 15:31:32', '2024-01-24 15:46:48', 2, 1.9, 1, 'N', 246, 161, 1, 14.9, 0, 0.5, 3.78, 0, 1, 22.68), +(2, '2024-01-24 15:50:45', '2024-01-24 16:22:14', 1, 6.82, 1, 'N', 162, 261, 1, 33.8, 0, 0.5, 3.78, 0, 1, 41.58), +(2, '2024-01-24 15:54:18', '2024-01-24 16:24:41', 1, 8.26, 1, 'N', 138, 262, 1, 37.3, 5, 0.5, 10.65, 6.94, 1, 65.64), +(2, '2024-01-24 15:11:02', '2024-01-24 15:33:35', 1, 1.6, 1, 'N', 162, 263, 1, 19.1, 0, 0.5, 4.62, 0, 1, 27.72), +(2, '2024-01-24 15:20:01', '2024-01-24 15:34:38', 2, 1.79, 1, 'N', 68, 163, 2, 14.2, 0, 0.5, 0, 0, 1, 18.2), +(2, '2024-01-24 15:50:36', '2024-01-24 15:59:20', 1, 0.58, 1, 'N', 162, 229, 1, 9.3, 0, 0.5, 3.33, 0, 1, 16.63), +(1, '2024-01-24 15:04:08', '2024-01-24 15:23:57', 1, 2, 1, 'N', 246, 161, 1, 14.9, 2.5, 0.5, 1, 0, 1, 19.9), +(1, '2024-01-24 15:25:27', '2024-01-24 15:37:29', 1, 1.6, 1, 'N', 161, 233, 1, 10.7, 2.5, 0.5, 3.65, 0, 1, 18.35), +(1, '2024-01-24 15:40:53', '2024-01-24 15:45:56', 1, 1.1, 1, 'Y', 233, 162, 1, 7.2, 2.5, 0.5, 2.24, 0, 1, 13.44), +(1, '2024-01-24 15:56:09', '2024-01-24 16:05:35', 1, 1.6, 1, 'N', 237, 239, 1, 10, 2.5, 0.5, 4.2, 0, 1, 18.2), +(2, '2024-01-24 15:03:07', '2024-01-24 15:21:19', 2, 5.73, 5, 'N', 180, 132, 1, 84, 0, 0, 17, 0, 1, 102), +(2, '2024-01-24 16:02:45', '2024-01-24 16:11:52', 1, 1.1, 1, 'N', 263, 141, 1, 10, 0, 0.5, 2.1, 0, 1, 16.1), +(2, '2024-01-24 15:19:51', '2024-01-24 15:30:56', 1, 0.77, 1, 'N', 162, 161, 1, 10.7, 0, 0.5, 2.94, 0, 1, 17.64), +(2, '2024-01-24 15:32:10', '2024-01-24 15:39:06', 1, 0.85, 1, 'N', 161, 170, 1, 7.9, 0, 0.5, 2.98, 0, 1, 14.88), +(2, '2024-01-24 15:44:04', '2024-01-24 15:56:43', 2, 1.07, 1, 'N', 170, 163, 1, 12.1, 0, 0.5, 3.22, 0, 1, 19.32), +(2, '2024-01-24 15:57:39', '2024-01-24 16:02:55', 1, 0.54, 1, 'N', 161, 237, 1, 6.5, 0, 0.5, 2.1, 0, 1, 12.6), +(1, '2024-01-24 15:04:50', '2024-01-24 15:25:58', 2, 2.9, 1, 'N', 161, 246, 1, 21.9, 2.5, 0.5, 5.15, 0, 1, 31.05), +(2, '2024-01-24 15:27:35', '2024-01-24 15:50:28', 1, 2.11, 1, 'N', 164, 79, 1, 20.5, 0, 0.5, 4.9, 0, 1, 29.4), +(2, '2024-01-24 15:13:53', '2024-01-24 15:55:09', 3, 5.62, 1, 'N', 161, 261, 1, 38, 0, 0.5, 8.4, 0, 1, 50.4), +(1, '2024-01-24 15:29:37', '2024-01-24 15:50:25', 1, 2.2, 1, 'N', 237, 230, 1, 18.4, 2.5, 0.5, 5.55, 0, 1, 27.95), +(1, '2024-01-24 15:34:29', '2024-01-24 15:45:41', 1, 2, 1, 'N', 142, 151, 1, 12.1, 2.5, 0.5, 3.22, 0, 1, 19.32), +(1, '2024-01-24 15:54:16', '2024-01-24 16:04:40', 2, 1.6, 1, 'N', 238, 143, 1, 10.7, 5, 0.5, 3.4, 0, 1, 20.6), +(2, '2024-01-24 15:05:20', '2024-01-24 15:16:38', 1, 1.27, 1, 'N', 142, 230, 2, 11.4, 0, 0.5, 0, 0, 1, 15.4), +(2, '2024-01-24 15:21:05', '2024-01-24 16:36:49', 1, 7.49, 1, 'N', 163, 181, 1, 61.8, 0, 0.5, 21.82, 6.94, 1, 94.56), +(2, '2024-01-24 15:13:19', '2024-01-24 15:28:32', 1, 2.51, 1, 'N', 143, 236, 1, 16.3, 0, 0.5, 4.06, 0, 1, 24.36), +(2, '2024-01-24 15:38:01', '2024-01-24 15:49:52', 1, 1.83, 1, 'N', 239, 262, 1, 12.8, 0, 0.5, 4.2, 0, 1, 21), +(2, '2024-01-24 15:09:19', '2024-01-24 15:26:41', 1, 2.42, 1, 'N', 238, 237, 1, 17, 0, 0.5, 4.2, 0, 1, 25.2), +(2, '2024-01-24 15:30:22', '2024-01-24 15:45:27', 1, 2.25, 1, 'N', 237, 233, 1, 15.6, 0, 0.5, 3.92, 0, 1, 23.52), +(1, '2024-01-24 15:57:50', '2024-01-24 16:45:02', 0, 15, 1, 'N', 138, 265, 2, 60.4, 9.25, 0.5, 0, 6.94, 1, 78.09), +(2, '2024-01-24 15:41:46', '2024-01-24 15:50:08', 1, 0.8, 1, 'N', 161, 100, 1, 8.6, 0, 0.5, 2.52, 0, 1, 15.12), +(2, '2024-01-24 15:54:22', '2024-01-24 15:59:06', 1, 0.5, 1, 'N', 100, 164, 2, 6.5, 0, 0.5, 0, 0, 1, 10.5), +(2, '2024-01-24 15:25:27', '2024-01-24 15:34:11', 2, 1.09, 1, 'N', 164, 234, 1, 9.3, 0, 0.5, 3.99, 0, 1, 17.29), +(2, '2024-01-24 15:14:18', '2024-01-24 15:22:17', 1, 0.78, 1, 'N', 234, 249, 1, 8.6, 0, 0.5, 2.52, 0, 1, 15.12), +(2, '2024-01-24 15:33:41', '2024-01-24 15:47:12', 1, 1.54, 1, 'N', 113, 231, 1, 12.8, 0, 0.5, 5.04, 0, 1, 21.84), +(2, '2024-01-24 15:53:15', '2024-01-24 16:04:11', 1, 1.63, 1, 'N', 125, 68, 1, 12.1, 0, 0.5, 2.42, 0, 1, 18.52), +(1, '2024-01-24 15:13:03', '2024-01-24 15:23:58', 1, 1.4, 1, 'N', 142, 161, 1, 10, 2.5, 0.5, 2.8, 0, 1, 16.8), +(1, '2024-01-24 15:31:49', '2024-01-24 15:46:47', 1, 1.8, 1, 'N', 161, 68, 1, 12.8, 2.5, 0.5, 3.36, 0, 1, 20.16), +(1, '2024-01-24 15:48:50', '2024-01-24 16:06:14', 1, 1.1, 1, 'N', 68, 246, 1, 12.1, 2.5, 0.5, 2, 0, 1, 18.1), +(2, '2024-01-24 15:17:46', '2024-01-24 15:28:19', 1, 1.02, 1, 'N', 236, 236, 1, 10.7, 0, 0.5, 3.67, 0, 1, 18.37), +(2, '2024-01-24 15:30:25', '2024-01-24 15:38:09', 1, 0.84, 1, 'N', 236, 141, 1, 8.6, 0, 0.5, 2.52, 0, 1, 15.12), +(2, '2024-01-24 15:47:13', '2024-01-24 15:50:30', 1, 0.54, 1, 'N', 237, 162, 1, 5.8, 0, 0.5, 2.45, 0, 1, 12.25), +(1, '2024-01-24 15:04:49', '2024-01-24 15:29:05', 1, 6.6, 1, 'N', 132, 134, 1, 27.5, 1.75, 0.5, 0, 0, 1, 30.75), +(1, '2024-01-24 15:52:43', '2024-01-24 16:48:43', 1, 16.3, 2, 'N', 132, 230, 1, 70, 4.25, 0.5, 15.15, 0, 1, 90.9), +(1, '2024-01-24 15:10:42', '2024-01-24 16:07:13', 1, 16.9, 2, 'N', 162, 132, 1, 70, 2.5, 0.5, 16.15, 6.94, 1, 97.09), +(1, '2024-01-24 15:24:26', '2024-01-24 15:53:43', 1, 3.1, 1, 'N', 236, 164, 2, 25.4, 2.5, 0.5, 0, 0, 1, 29.4), +(1, '2024-01-24 15:55:46', '2024-01-24 16:02:04', 1, 0.8, 1, 'N', 164, 107, 1, 7.9, 2.5, 0.5, 2.35, 0, 1, 14.25), +(1, '2024-01-24 15:57:50', '2024-01-24 16:21:27', 1, 2.9, 1, 'N', 75, 143, 1, 21.9, 5, 0.5, 5.65, 0, 1, 34.05), +(2, '2024-01-24 15:56:42', '2024-01-24 16:01:57', 1, 0.73, 1, 'N', 237, 162, 2, 7.2, 0, 0.5, 0, 0, 1, 11.2), +(2, '2024-01-24 15:02:26', '2024-01-24 15:14:20', 1, 1.41, 1, 'N', 151, 41, 2, 12.1, 0, 0.5, 0, 0, 1, 13.6), +(2, '2024-01-24 15:43:11', '2024-01-24 15:52:26', 1, 2.03, 1, 'N', 75, 239, 1, 12.1, 0, 0.5, 3.22, 0, 1, 19.32), +(1, '2024-01-24 15:09:57', '2024-01-24 15:17:06', 1, 0.9, 1, 'N', 186, 234, 1, 8.6, 2.5, 0.5, 1.5, 0, 1, 14.1), +(1, '2024-01-24 15:15:44', '2024-01-24 16:03:27', 1, 5.2, 1, 'N', 234, 41, 2, 40.8, 2.5, 0.5, 0, 0, 1, 44.8), +(2, '2024-01-24 15:03:30', '2024-01-24 15:15:18', 1, 1.74, 1, 'N', 142, 162, 1, 12.8, 0, 0.5, 3, 0, 1, 19.8), +(2, '2024-01-24 15:16:18', '2024-01-24 15:26:54', 1, 1.02, 1, 'N', 162, 230, 1, 10.7, 0, 0.5, 2.94, 0, 1, 17.64), +(1, '2024-01-24 15:09:12', '2024-01-24 15:26:06', 1, 2.5, 1, 'N', 163, 43, 2, 15.6, 2.5, 0.5, 0, 0, 1, 19.6), +(1, '2024-01-24 15:36:01', '2024-01-24 16:09:08', 1, 3.4, 1, 'N', 238, 164, 1, 26.8, 2.5, 0.5, 3.08, 0, 1, 33.88), +(1, '2024-01-24 15:01:40', '2024-01-24 15:30:58', 1, 4, 1, 'N', 231, 181, 1, 23.3, 2.5, 0.5, 6.85, 0, 1, 34.15), +(1, '2024-01-24 15:44:58', '2024-01-24 16:02:01', 1, 1, 1, 'N', 97, 33, 2, 13.5, 0, 0.5, 0, 0, 1, 15), +(1, '2024-01-24 15:08:08', '2024-01-24 15:19:26', 1, 1.1, 1, 'N', 262, 75, 2, 10.7, 2.5, 0.5, 0, 0, 1, 14.7), +(1, '2024-01-24 15:24:26', '2024-01-24 15:51:30', 1, 2.8, 1, 'N', 75, 48, 1, 21.9, 2.5, 0.5, 5.2, 0, 1, 31.1), +(1, '2024-01-24 15:05:32', '2024-01-24 16:11:42', 1, 8.1, 1, 'N', 186, 85, 2, 49.2, 2.5, 0.5, 0, 0, 1, 53.2), +(1, '2024-01-24 15:16:02', '2024-01-24 15:25:14', 1, 0.5, 1, 'N', 162, 161, 1, 9.3, 2.5, 0.5, 2.65, 0, 1, 15.95), +(1, '2024-01-24 15:29:34', '2024-01-24 15:34:45', 1, 0.3, 1, 'N', 161, 162, 2, 6.5, 2.5, 0.5, 0, 0, 1, 10.5), +(1, '2024-01-24 15:56:23', '2024-01-24 16:12:18', 1, 1.4, 1, 'N', 48, 164, 1, 14.9, 2.5, 0.5, 3.75, 0, 1, 22.65), +(1, '2024-01-24 15:22:06', '2024-01-24 15:46:23', 1, 4.4, 1, 'N', 68, 238, 1, 26.1, 2.5, 0.5, 7.5, 0, 1, 37.6), +(2, '2024-01-24 15:28:46', '2024-01-24 15:43:33', 1, 1.49, 1, 'N', 113, 186, 1, 13.5, 0, 0.5, 3.5, 0, 1, 21), +(2, '2024-01-24 15:49:11', '2024-01-24 16:03:14', 1, 1.49, 1, 'N', 90, 161, 1, 13.5, 0, 0.5, 3.5, 0, 1, 21), +(1, '2024-01-24 15:09:45', '2024-01-24 15:43:41', 1, 2.6, 1, 'N', 158, 170, 1, 28.2, 2.5, 0.5, 6.4, 0, 1, 38.6), +(2, '2024-01-24 15:10:12', '2024-01-24 15:30:12', 1, 2.64, 1, 'N', 186, 141, 1, 19.1, 0, 0.5, 2, 0, 1, 25.1), +(2, '2024-01-24 15:08:02', '2024-01-24 15:20:36', 1, 1.59, 1, 'N', 142, 161, 1, 13.5, 0, 0.5, 3.5, 0, 1, 21), +(2, '2024-01-24 15:54:25', '2024-01-24 16:25:45', 1, 3.55, 1, 'N', 236, 234, 1, 27.5, 0, 0.5, 6.3, 0, 1, 37.8), +(2, '2024-01-24 15:09:55', '2024-01-24 15:22:14', 1, 1.85, 1, 'N', 236, 143, 1, 13.5, 0, 0.5, 2, 0, 1, 19.5), +(2, '2024-01-24 15:33:37', '2024-01-24 15:39:20', 2, 0.59, 1, 'N', 238, 238, 1, 7.2, 0, 0.5, 2.24, 0, 1, 13.44), +(2, '2024-01-24 15:58:14', '2024-01-24 16:02:46', 2, 0.42, 1, 'N', 239, 142, 1, 5.8, 0, 0.5, 1.96, 0, 1, 11.76), +(2, '2024-01-24 15:05:34', '2024-01-24 15:51:33', 1, 11.54, 1, 'N', 138, 142, 1, 52, 5, 0.5, 13.94, 6.94, 1, 83.63), +(2, '2024-01-24 15:19:22', '2024-01-24 15:28:49', 1, 1.38, 1, 'N', 230, 143, 1, 10.7, 0, 0.5, 1.47, 0, 1, 16.17), +(2, '2024-01-24 15:22:30', '2024-01-24 15:47:17', 1, 3.6, 1, 'N', 163, 74, 2, 22.6, 0, 0.5, 0, 0, 1, 26.6), +(1, '2024-01-24 15:51:41', '2024-01-24 15:54:17', 1, 0.3, 1, 'N', 249, 90, 1, 4.4, 5, 0.5, 2, 0, 1, 12.9), +(2, '2024-01-24 15:02:26', '2024-01-24 15:07:59', 1, 0.66, 1, 'N', 161, 163, 1, 7.2, 0, 0.5, 2.24, 0, 1, 13.44), +(2, '2024-01-24 15:09:01', '2024-01-24 15:25:34', 1, 1.38, 1, 'N', 163, 236, 1, 14.9, 0, 0.5, 1, 0, 1, 19.9), +(1, '2024-01-24 15:06:58', '2024-01-24 15:24:35', 1, 1.4, 1, 'N', 236, 161, 1, 14.9, 2.5, 0.5, 3.8, 0, 1, 22.7), +(1, '2024-01-24 15:39:09', '2024-01-24 16:03:25', 1, 2.5, 1, 'N', 233, 68, 1, 19.8, 2.5, 0.5, 4.75, 0, 1, 28.55), +(2, '2024-01-24 15:21:47', '2024-01-24 15:55:15', 1, 8.65, 1, 'N', 70, 230, 2, 40.8, 5, 0.5, 0, 6.94, 1, 58.49), +(2, '2024-01-24 15:32:46', '2024-01-24 16:01:04', 1, 2.16, 1, 'N', 113, 79, 1, 23.3, 0, 0.5, 8.19, 0, 1, 35.49), +(2, '2024-01-24 15:37:00', '2024-01-24 16:01:28', 1, 4.56, 1, 'N', 261, 170, 1, 25.4, 0, 0.5, 5.88, 0, 1, 35.28); + + +# test explain heap scan +query T +EXPLAIN SELECT COUNT(*) FROM nyc_trips WHERE tip_amount <> 0 +---- +Aggregate (cost=16.89..16.91 rows=1 width=8) + -> Seq Scan on nyc_trips (cost=0.00..15.75 rows=458 width=0) + Filter: (tip_amount <> '0'::double precision)